feat(ui): redesign UI with Tailwind CSS, CodeMirror 6, and new layout
- Replace all hand-written CSS with Tailwind utility classes - Upgrade CodeMirror 5 to CodeMirror 6 (scoped @codemirror/* packages) - Redesign layout with responsive sidebar navigation using JSX/Mithril - Flatten admin routes (e.g. /admin/presets -> /presets) - Add "remember me" login option with 2-week persistent cookie - Add delete confirmation dialogs across admin pages - Upgrade esbuild from 0.21 to 0.27 and update browser targets - Add Inter and Roboto Mono variable fonts with subset script
This commit is contained in:
1 parent
a7256620e0
commit
4a191afdec
64 files changed
+4147
-2421
No files matched your search
+50
-25
@@ -1,4 +1,3 @@
|
||||
import { Editor } from "codemirror";
|
||||
import { ClosureComponent } from "mithril";
|
||||
import { m } from "./components.ts";
|
||||
import { codeMirror } from "./dynamic-loader.ts";
|
||||
@@ -11,41 +10,67 @@ interface Attrs {
|
||||
focus?: boolean;
|
||||
onSubmit?: (dom: Element) => void;
|
||||
onChange?: (value: string) => void;
|
||||
onReady?: (editor: Editor) => void;
|
||||
}
|
||||
|
||||
const component: ClosureComponent<Attrs> = () => {
|
||||
return {
|
||||
view: (vnode) => {
|
||||
return m("textarea", {
|
||||
name: vnode.attrs.id,
|
||||
value: vnode.attrs.value,
|
||||
return m("div.font-mono text-sm", {
|
||||
oncreate: (_vnode) => {
|
||||
const editor = codeMirror.fromTextArea(
|
||||
_vnode.dom as HTMLTextAreaElement,
|
||||
{
|
||||
mode: vnode.attrs.mode,
|
||||
lineNumbers: true,
|
||||
readOnly: vnode.attrs.readOnly,
|
||||
extraKeys: {
|
||||
"Ctrl-Enter": () => {
|
||||
if (vnode.attrs.onSubmit) vnode.attrs.onSubmit(_vnode.dom);
|
||||
},
|
||||
"Cmd-Enter": () => {
|
||||
if (vnode.attrs.onSubmit) vnode.attrs.onSubmit(_vnode.dom);
|
||||
},
|
||||
const theme = codeMirror.EditorView.theme({
|
||||
"&.cm-editor": {
|
||||
display: "block",
|
||||
width: "50rem",
|
||||
height: "30rem",
|
||||
"max-width": "100%",
|
||||
"border-radius": "0.375rem",
|
||||
"border-width": "1px",
|
||||
"border-color": "var(--color-stone-300)",
|
||||
"box-shadow":
|
||||
"var(--tw-ring-shadow, 0 0 #0000), 0 1px 2px 0 rgb(0 0 0 / 0.05)",
|
||||
overflow: "hidden",
|
||||
"& > .cm-scroller": {
|
||||
"font-family": "inherit",
|
||||
"line-height": "inherit",
|
||||
},
|
||||
},
|
||||
);
|
||||
"&.cm-editor.cm-focused": {
|
||||
outline: "none",
|
||||
"border-color": "var(--color-cyan-500)",
|
||||
"--tw-ring-shadow": "0 0 0 1px var(--color-cyan-500)",
|
||||
},
|
||||
});
|
||||
|
||||
if (vnode.attrs.onChange) {
|
||||
editor.on("change", (e) => {
|
||||
vnode.attrs.onChange(e.getValue());
|
||||
});
|
||||
}
|
||||
const extensions = [
|
||||
theme,
|
||||
codeMirror.lineNumbers(),
|
||||
codeMirror.history(),
|
||||
codeMirror.syntaxHighlighting(codeMirror.defaultHighlightStyle),
|
||||
codeMirror.keymap.of([
|
||||
...codeMirror.defaultKeymap,
|
||||
...codeMirror.historyKeymap,
|
||||
]),
|
||||
codeMirror.EditorState.readOnly.of(!!vnode.attrs.readOnly),
|
||||
codeMirror.EditorView.updateListener.of((update) => {
|
||||
if (update.docChanged && vnode.attrs.onChange)
|
||||
vnode.attrs.onChange(update.state.doc.toString());
|
||||
}),
|
||||
];
|
||||
|
||||
if (vnode.attrs.mode === "javascript")
|
||||
extensions.push(codeMirror.javascript());
|
||||
else if (vnode.attrs.mode === "yaml")
|
||||
extensions.push(codeMirror.yaml());
|
||||
|
||||
const editor = new codeMirror.EditorView({
|
||||
state: codeMirror.EditorState.create({
|
||||
doc: vnode.attrs.value,
|
||||
extensions,
|
||||
}),
|
||||
parent: _vnode.dom as HTMLTextAreaElement,
|
||||
});
|
||||
|
||||
if (vnode.attrs.focus) editor.focus();
|
||||
if (vnode.attrs.onReady) vnode.attrs.onReady(editor);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user