@invana/editor 0.0.31 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +15 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +16 -5
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -8,6 +8,7 @@ var python = require('@codemirror/legacy-modes/mode/python');
|
|
|
8
8
|
var shell = require('@codemirror/legacy-modes/mode/shell');
|
|
9
9
|
var javascript = require('@codemirror/legacy-modes/mode/javascript');
|
|
10
10
|
var cypher = require('@codemirror/legacy-modes/mode/cypher');
|
|
11
|
+
var yaml = require('@codemirror/legacy-modes/mode/yaml');
|
|
11
12
|
var ui = require('@invana/ui');
|
|
12
13
|
var jsxRuntime = require('react/jsx-runtime');
|
|
13
14
|
var commands = require('@codemirror/commands');
|
|
@@ -45,7 +46,9 @@ function useCodeMirror({
|
|
|
45
46
|
onChangeRef.current = onChange;
|
|
46
47
|
}, [onChange]);
|
|
47
48
|
const extensionsRef = React__namespace.useRef(extensions);
|
|
48
|
-
|
|
49
|
+
React__namespace.useLayoutEffect(() => {
|
|
50
|
+
extensionsRef.current = extensions;
|
|
51
|
+
}, [extensions]);
|
|
49
52
|
const compartment = React__namespace.useRef(new state.Compartment());
|
|
50
53
|
React__namespace.useEffect(() => {
|
|
51
54
|
if (!host.current) return;
|
|
@@ -87,6 +90,7 @@ var MODES = {
|
|
|
87
90
|
javascript: () => language.StreamLanguage.define(javascript.javascript),
|
|
88
91
|
json: () => language.StreamLanguage.define(javascript.json),
|
|
89
92
|
cypher: () => language.StreamLanguage.define(cypher.cypher),
|
|
93
|
+
yaml: () => language.StreamLanguage.define(yaml.yaml),
|
|
90
94
|
plain: () => null
|
|
91
95
|
};
|
|
92
96
|
function CodeBlock({
|
|
@@ -136,6 +140,7 @@ function MarkdownEditorBlock({
|
|
|
136
140
|
onChange,
|
|
137
141
|
readOnly,
|
|
138
142
|
version,
|
|
143
|
+
placeholder,
|
|
139
144
|
className,
|
|
140
145
|
...props
|
|
141
146
|
}) {
|
|
@@ -148,9 +153,10 @@ function MarkdownEditorBlock({
|
|
|
148
153
|
view.EditorView.lineWrapping,
|
|
149
154
|
commands.history(),
|
|
150
155
|
view.keymap.of([...commands.defaultKeymap, ...commands.historyKeymap]),
|
|
151
|
-
view.EditorView.editable.of(!readOnly)
|
|
156
|
+
view.EditorView.editable.of(!readOnly),
|
|
157
|
+
...placeholder ? [view.placeholder(placeholder)] : []
|
|
152
158
|
],
|
|
153
|
-
[readOnly]
|
|
159
|
+
[readOnly, placeholder]
|
|
154
160
|
);
|
|
155
161
|
const { ref } = useCodeMirror({ value, extensions, onChange });
|
|
156
162
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.cn("relative border border-border bg-muted/40", className), ...props, children: [
|
|
@@ -161,7 +167,12 @@ function MarkdownEditorBlock({
|
|
|
161
167
|
ref,
|
|
162
168
|
className: ui.cn(
|
|
163
169
|
"[&_.cm-editor]:bg-transparent [&_.cm-content]:font-mono [&_.cm-content]:text-sm",
|
|
164
|
-
"[&_.cm-focused]:outline-none [&_.cm-cursor]:border-foreground"
|
|
170
|
+
"[&_.cm-focused]:outline-none [&_.cm-cursor]:border-foreground",
|
|
171
|
+
"[&_.cm-placeholder]:text-muted-foreground",
|
|
172
|
+
// Room for the version marker, so it never sits over the first line.
|
|
173
|
+
// On the host, not `.cm-content`: CodeMirror's own theme sets that
|
|
174
|
+
// padding and wins.
|
|
175
|
+
version != null && "pr-12"
|
|
165
176
|
)
|
|
166
177
|
}
|
|
167
178
|
)
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/use-code-mirror.ts","../src/code-block.tsx","../src/markdown-editor-block.tsx"],"names":["React","view","Compartment","EditorView","EditorState","StreamLanguage","python","shell","javascript","json","cypher","language","React2","syntaxHighlighting","defaultHighlightStyle","lineNumbers","jsx","cn","React3","history","keymap","defaultKeymap","historyKeymap","jsxs"],"mappings":"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","file":"index.cjs","sourcesContent":["import * as React from \"react\"\nimport { Compartment, EditorState, type Extension } from \"@codemirror/state\"\nimport { EditorView } from \"@codemirror/view\"\n\nexport interface UseCodeMirrorOptions {\n value: string\n extensions: Extension[]\n onChange?: (value: string) => void\n}\n\n/**\n * Mounts a CodeMirror view into a ref'd element and keeps it in sync.\n *\n * The whole reason this is a hook rather than a component: CodeMirror owns its\n * own DOM, so React must not re-render into it. Value changes are pushed as\n * transactions, and only when they actually differ — writing the view's own\n * output back into it would move the cursor to the end on every keystroke.\n */\nexport function useCodeMirror({\n value,\n extensions,\n onChange,\n}: UseCodeMirrorOptions) {\n const host = React.useRef<HTMLDivElement | null>(null)\n const view = React.useRef<EditorView | null>(null)\n\n // Keep the latest onChange without tearing down the view when it changes\n // identity — a caller passing an inline arrow would otherwise remount the\n // editor on every render.\n const onChangeRef = React.useRef(onChange)\n React.useEffect(() => {\n onChangeRef.current = onChange\n }, [onChange])\n\n // The view is created once and reconfigured in place. Keying the effect on\n // `extensions` identity looked simpler, but a caller passing an inline array\n // would then rebuild the editor on every render — losing the cursor, the\n // selection and focus mid-keystroke. \"Callers are expected to memoise\" is a\n // trap, not a contract, so the hook does not depend on it.\n const extensionsRef = React.useRef(extensions)\n extensionsRef.current = extensions\n\n const compartment = React.useRef(new Compartment())\n\n React.useEffect(() => {\n if (!host.current) return\n const v = new EditorView({\n state: EditorState.create({\n doc: value,\n extensions: [\n compartment.current.of(extensionsRef.current),\n EditorView.updateListener.of((u) => {\n if (u.docChanged) onChangeRef.current?.(u.state.doc.toString())\n }),\n ],\n }),\n parent: host.current,\n })\n view.current = v\n return () => {\n v.destroy()\n view.current = null\n }\n // Mount once. Configuration changes go through the compartment below.\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Swap configuration without touching the document — this is what a\n // Compartment is for, and it is why toggling `readOnly` or a language keeps\n // the caret where it was.\n React.useEffect(() => {\n const v = view.current\n if (!v) return\n v.dispatch({ effects: compartment.current.reconfigure(extensions) })\n }, [extensions])\n\n React.useEffect(() => {\n const v = view.current\n if (!v) return\n const current = v.state.doc.toString()\n if (current === value) return\n v.dispatch({ changes: { from: 0, to: current.length, insert: value } })\n }, [value])\n\n return { ref: host, view }\n}\n","import * as React from \"react\"\nimport { EditorView, lineNumbers } from \"@codemirror/view\"\nimport { EditorState } from \"@codemirror/state\"\nimport {\n StreamLanguage,\n syntaxHighlighting,\n defaultHighlightStyle,\n} from \"@codemirror/language\"\nimport { python } from \"@codemirror/legacy-modes/mode/python\"\nimport { shell } from \"@codemirror/legacy-modes/mode/shell\"\nimport { javascript, json } from \"@codemirror/legacy-modes/mode/javascript\"\nimport { cypher } from \"@codemirror/legacy-modes/mode/cypher\"\nimport { cn } from \"@invana/ui\"\n\nimport { useCodeMirror } from \"./use-code-mirror\"\n\nexport type CodeLanguage =\n | \"python\"\n | \"shell\"\n | \"javascript\"\n | \"json\"\n | \"cypher\"\n | \"plain\"\n\nexport interface CodeBlockProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n value: string\n language?: CodeLanguage\n /** Line numbers are off by default — a five-line snippet does not need a gutter. */\n showLineNumbers?: boolean\n /**\n * Cap the block's height in px and let it scroll inside that, for a document\n * whose length is not the point — a run's `result.json`, an exported model.\n *\n * Unset, the block is as tall as its content, which is right for a snippet\n * and wrong for a record that grows as a run proceeds.\n */\n maxHeight?: number\n}\n\nconst MODES = {\n python: () => StreamLanguage.define(python),\n shell: () => StreamLanguage.define(shell),\n javascript: () => StreamLanguage.define(javascript),\n json: () => StreamLanguage.define(json),\n cypher: () => StreamLanguage.define(cypher),\n plain: () => null,\n}\n\n/**\n * Code, shown as it is — read-only.\n *\n * The DAG in a hand-off, a query in a diagnosis, an exported model. It is not a\n * disabled editor: `EditorState.readOnly` plus a non-editable view means there\n * is no cursor to place and no illusion that typing would do something.\n */\nexport function CodeBlock({\n value,\n language = \"plain\",\n showLineNumbers,\n maxHeight,\n className,\n style,\n ...props\n}: CodeBlockProps) {\n const extensions = React.useMemo(() => {\n const mode = MODES[language]()\n return [\n // StreamLanguage only produces tokens; without a highlight style nothing\n // paints them and the block renders as flat text.\n syntaxHighlighting(defaultHighlightStyle, { fallback: true }),\n EditorView.editable.of(false),\n EditorState.readOnly.of(true),\n EditorView.lineWrapping,\n ...(showLineNumbers ? [lineNumbers()] : []),\n ...(mode ? [mode] : []),\n // The scroller lives inside CodeMirror rather than on the wrapper, so the\n // editor knows its own viewport and does not render every line of a long\n // document into a box that then clips them.\n ...(maxHeight != null\n ? [EditorView.theme({ \"&\": { maxHeight: `${maxHeight}px` } })]\n : []),\n ]\n }, [language, showLineNumbers, maxHeight])\n\n const { ref } = useCodeMirror({ value, extensions })\n\n return (\n <div\n ref={ref}\n className={cn(\n \"overflow-hidden border border-border bg-muted/40 [&_.cm-editor]:bg-transparent\",\n \"[&_.cm-content]:font-mono [&_.cm-content]:text-sm [&_.cm-focused]:outline-none\",\n \"[&_.cm-gutters]:border-none [&_.cm-gutters]:bg-transparent [&_.cm-gutters]:text-muted-foreground\",\n className,\n )}\n style={style}\n {...props}\n />\n )\n}\n","import * as React from \"react\"\nimport { EditorView } from \"@codemirror/view\"\nimport { history, defaultKeymap, historyKeymap } from \"@codemirror/commands\"\nimport { keymap } from \"@codemirror/view\"\nimport { cn } from \"@invana/ui\"\n\nimport { useCodeMirror } from \"./use-code-mirror\"\n\nexport interface MarkdownEditorBlockProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\" | \"children\"> {\n value: string\n onChange?: (value: string) => void\n readOnly?: boolean\n /** A version marker shown in the corner — `v5`, `v7 · editing`. */\n version?: React.ReactNode\n placeholder?: string\n}\n\n/**\n * The prose a person writes for an agent to read — graph rules, a skill's\n * content, an acceptance note.\n *\n * Plain markdown in a monospace field, not a rich-text editor. What is typed\n * here is offered verbatim in a model's prompt, so what you see has to be\n * exactly what the agent gets; a WYSIWYG layer would put a translation step\n * between the author and the thing that runs.\n *\n * `version` sits in the corner because these documents are versioned and a\n * running thinking finishes on the version it started with — editing without\n * seeing which version you are on is how two people overwrite each other.\n */\nexport function MarkdownEditorBlock({\n value,\n onChange,\n readOnly,\n version,\n className,\n ...props\n}: MarkdownEditorBlockProps) {\n const extensions = React.useMemo(\n () => [\n // No markdown syntax mode, deliberately. What is typed here is offered\n // verbatim in a model's prompt, and highlighting it would be decoration\n // on text whose whole contract is that it is plain. It also keeps this\n // package's dependencies to the CodeMirror set the product already ships.\n EditorView.lineWrapping,\n history(),\n keymap.of([...defaultKeymap, ...historyKeymap]),\n EditorView.editable.of(!readOnly),\n ],\n [readOnly],\n )\n\n const { ref } = useCodeMirror({ value, extensions, onChange })\n\n return (\n <div className={cn(\"relative border border-border bg-muted/40\", className)} {...props}>\n {version != null ? (\n <span className=\"absolute right-1 top-1 z-10 border border-border bg-card px-1 text-sm text-muted-foreground\">\n {version}\n </span>\n ) : null}\n <div\n ref={ref}\n className={cn(\n \"[&_.cm-editor]:bg-transparent [&_.cm-content]:font-mono [&_.cm-content]:text-sm\",\n \"[&_.cm-focused]:outline-none [&_.cm-cursor]:border-foreground\",\n )}\n />\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/use-code-mirror.ts","../src/code-block.tsx","../src/markdown-editor-block.tsx"],"names":["React","view","Compartment","EditorView","EditorState","StreamLanguage","python","shell","javascript","json","cypher","yaml","language","React2","syntaxHighlighting","defaultHighlightStyle","lineNumbers","jsx","cn","React3","history","keymap","defaultKeymap","historyKeymap","cmPlaceholder","jsxs"],"mappings":"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","file":"index.cjs","sourcesContent":["import * as React from \"react\"\nimport { Compartment, EditorState, type Extension } from \"@codemirror/state\"\nimport { EditorView } from \"@codemirror/view\"\n\nexport interface UseCodeMirrorOptions {\n value: string\n extensions: Extension[]\n onChange?: (value: string) => void\n}\n\n/**\n * Mounts a CodeMirror view into a ref'd element and keeps it in sync.\n *\n * The whole reason this is a hook rather than a component: CodeMirror owns its\n * own DOM, so React must not re-render into it. Value changes are pushed as\n * transactions, and only when they actually differ — writing the view's own\n * output back into it would move the cursor to the end on every keystroke.\n */\nexport function useCodeMirror({\n value,\n extensions,\n onChange,\n}: UseCodeMirrorOptions) {\n const host = React.useRef<HTMLDivElement | null>(null)\n const view = React.useRef<EditorView | null>(null)\n\n // Keep the latest onChange without tearing down the view when it changes\n // identity — a caller passing an inline arrow would otherwise remount the\n // editor on every render.\n const onChangeRef = React.useRef(onChange)\n React.useEffect(() => {\n onChangeRef.current = onChange\n }, [onChange])\n\n // The view is created once and reconfigured in place. Keying the effect on\n // `extensions` identity looked simpler, but a caller passing an inline array\n // would then rebuild the editor on every render — losing the cursor, the\n // selection and focus mid-keystroke. \"Callers are expected to memoise\" is a\n // trap, not a contract, so the hook does not depend on it.\n const extensionsRef = React.useRef(extensions)\n // Synced after commit, before the effects below read it — never mid-render.\n React.useLayoutEffect(() => {\n extensionsRef.current = extensions\n }, [extensions])\n\n const compartment = React.useRef(new Compartment())\n\n React.useEffect(() => {\n if (!host.current) return\n const v = new EditorView({\n state: EditorState.create({\n doc: value,\n extensions: [\n compartment.current.of(extensionsRef.current),\n EditorView.updateListener.of((u) => {\n if (u.docChanged) onChangeRef.current?.(u.state.doc.toString())\n }),\n ],\n }),\n parent: host.current,\n })\n view.current = v\n return () => {\n v.destroy()\n view.current = null\n }\n // Mount once. Configuration changes go through the compartment below.\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Swap configuration without touching the document — this is what a\n // Compartment is for, and it is why toggling `readOnly` or a language keeps\n // the caret where it was.\n React.useEffect(() => {\n const v = view.current\n if (!v) return\n v.dispatch({ effects: compartment.current.reconfigure(extensions) })\n }, [extensions])\n\n React.useEffect(() => {\n const v = view.current\n if (!v) return\n const current = v.state.doc.toString()\n if (current === value) return\n v.dispatch({ changes: { from: 0, to: current.length, insert: value } })\n }, [value])\n\n return { ref: host, view }\n}\n","import * as React from \"react\"\nimport { EditorView, lineNumbers } from \"@codemirror/view\"\nimport { EditorState } from \"@codemirror/state\"\nimport {\n StreamLanguage,\n syntaxHighlighting,\n defaultHighlightStyle,\n} from \"@codemirror/language\"\nimport { python } from \"@codemirror/legacy-modes/mode/python\"\nimport { shell } from \"@codemirror/legacy-modes/mode/shell\"\nimport { javascript, json } from \"@codemirror/legacy-modes/mode/javascript\"\nimport { cypher } from \"@codemirror/legacy-modes/mode/cypher\"\nimport { yaml } from \"@codemirror/legacy-modes/mode/yaml\"\nimport { cn } from \"@invana/ui\"\n\nimport { useCodeMirror } from \"./use-code-mirror\"\n\nexport type CodeLanguage =\n | \"python\"\n | \"shell\"\n | \"javascript\"\n | \"json\"\n | \"cypher\"\n | \"yaml\"\n | \"plain\"\n\nexport interface CodeBlockProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n value: string\n language?: CodeLanguage\n /** Line numbers are off by default — a five-line snippet does not need a gutter. */\n showLineNumbers?: boolean\n /**\n * Cap the block's height in px and let it scroll inside that, for a document\n * whose length is not the point — a run's `result.json`, an exported model.\n *\n * Unset, the block is as tall as its content, which is right for a snippet\n * and wrong for a record that grows as a run proceeds.\n */\n maxHeight?: number\n}\n\nconst MODES = {\n python: () => StreamLanguage.define(python),\n shell: () => StreamLanguage.define(shell),\n javascript: () => StreamLanguage.define(javascript),\n json: () => StreamLanguage.define(json),\n cypher: () => StreamLanguage.define(cypher),\n yaml: () => StreamLanguage.define(yaml),\n plain: () => null,\n}\n\n/**\n * Code, shown as it is — read-only.\n *\n * The DAG in a hand-off, a query in a diagnosis, an exported model. It is not a\n * disabled editor: `EditorState.readOnly` plus a non-editable view means there\n * is no cursor to place and no illusion that typing would do something.\n */\nexport function CodeBlock({\n value,\n language = \"plain\",\n showLineNumbers,\n maxHeight,\n className,\n style,\n ...props\n}: CodeBlockProps) {\n const extensions = React.useMemo(() => {\n const mode = MODES[language]()\n return [\n // StreamLanguage only produces tokens; without a highlight style nothing\n // paints them and the block renders as flat text.\n syntaxHighlighting(defaultHighlightStyle, { fallback: true }),\n EditorView.editable.of(false),\n EditorState.readOnly.of(true),\n EditorView.lineWrapping,\n ...(showLineNumbers ? [lineNumbers()] : []),\n ...(mode ? [mode] : []),\n // The scroller lives inside CodeMirror rather than on the wrapper, so the\n // editor knows its own viewport and does not render every line of a long\n // document into a box that then clips them.\n ...(maxHeight != null\n ? [EditorView.theme({ \"&\": { maxHeight: `${maxHeight}px` } })]\n : []),\n ]\n }, [language, showLineNumbers, maxHeight])\n\n const { ref } = useCodeMirror({ value, extensions })\n\n return (\n <div\n ref={ref}\n className={cn(\n \"overflow-hidden border border-border bg-muted/40 [&_.cm-editor]:bg-transparent\",\n \"[&_.cm-content]:font-mono [&_.cm-content]:text-sm [&_.cm-focused]:outline-none\",\n \"[&_.cm-gutters]:border-none [&_.cm-gutters]:bg-transparent [&_.cm-gutters]:text-muted-foreground\",\n className,\n )}\n style={style}\n {...props}\n />\n )\n}\n","import * as React from \"react\"\nimport { EditorView } from \"@codemirror/view\"\nimport { history, defaultKeymap, historyKeymap } from \"@codemirror/commands\"\nimport { keymap, placeholder as cmPlaceholder } from \"@codemirror/view\"\nimport { cn } from \"@invana/ui\"\n\nimport { useCodeMirror } from \"./use-code-mirror\"\n\nexport interface MarkdownEditorBlockProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\" | \"children\"> {\n value: string\n onChange?: (value: string) => void\n readOnly?: boolean\n /** A version marker shown in the corner — `v5`, `v7 · editing`. */\n version?: React.ReactNode\n /**\n * Drawn in the empty editor, never stored — what the reader gets when\n * nothing is written, such as an agent's default voice.\n */\n placeholder?: string\n}\n\n/**\n * The prose a person writes for an agent to read — graph rules, a skill's\n * content, an acceptance note.\n *\n * Plain markdown in a monospace field, not a rich-text editor. What is typed\n * here is offered verbatim in a model's prompt, so what you see has to be\n * exactly what the agent gets; a WYSIWYG layer would put a translation step\n * between the author and the thing that runs.\n *\n * `version` sits in the corner because these documents are versioned and a\n * running thinking finishes on the version it started with — editing without\n * seeing which version you are on is how two people overwrite each other.\n */\nexport function MarkdownEditorBlock({\n value,\n onChange,\n readOnly,\n version,\n placeholder,\n className,\n ...props\n}: MarkdownEditorBlockProps) {\n const extensions = React.useMemo(\n () => [\n // No markdown syntax mode, deliberately. What is typed here is offered\n // verbatim in a model's prompt, and highlighting it would be decoration\n // on text whose whole contract is that it is plain. It also keeps this\n // package's dependencies to the CodeMirror set the product already ships.\n EditorView.lineWrapping,\n history(),\n keymap.of([...defaultKeymap, ...historyKeymap]),\n EditorView.editable.of(!readOnly),\n ...(placeholder ? [cmPlaceholder(placeholder)] : []),\n ],\n [readOnly, placeholder],\n )\n\n const { ref } = useCodeMirror({ value, extensions, onChange })\n\n return (\n <div className={cn(\"relative border border-border bg-muted/40\", className)} {...props}>\n {version != null ? (\n <span className=\"absolute right-1 top-1 z-10 border border-border bg-card px-1 text-sm text-muted-foreground\">\n {version}\n </span>\n ) : null}\n <div\n ref={ref}\n className={cn(\n \"[&_.cm-editor]:bg-transparent [&_.cm-content]:font-mono [&_.cm-content]:text-sm\",\n \"[&_.cm-focused]:outline-none [&_.cm-cursor]:border-foreground\",\n \"[&_.cm-placeholder]:text-muted-foreground\",\n // Room for the version marker, so it never sits over the first line.\n // On the host, not `.cm-content`: CodeMirror's own theme sets that\n // padding and wins.\n version != null && \"pr-12\",\n )}\n />\n </div>\n )\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -2,7 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
import { Extension } from '@codemirror/state';
|
|
3
3
|
import { EditorView } from '@codemirror/view';
|
|
4
4
|
|
|
5
|
-
type CodeLanguage = "python" | "shell" | "javascript" | "json" | "cypher" | "plain";
|
|
5
|
+
type CodeLanguage = "python" | "shell" | "javascript" | "json" | "cypher" | "yaml" | "plain";
|
|
6
6
|
interface CodeBlockProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
|
|
7
7
|
value: string;
|
|
8
8
|
language?: CodeLanguage;
|
|
@@ -32,6 +32,10 @@ interface MarkdownEditorBlockProps extends Omit<React.HTMLAttributes<HTMLDivElem
|
|
|
32
32
|
readOnly?: boolean;
|
|
33
33
|
/** A version marker shown in the corner — `v5`, `v7 · editing`. */
|
|
34
34
|
version?: React.ReactNode;
|
|
35
|
+
/**
|
|
36
|
+
* Drawn in the empty editor, never stored — what the reader gets when
|
|
37
|
+
* nothing is written, such as an agent's default voice.
|
|
38
|
+
*/
|
|
35
39
|
placeholder?: string;
|
|
36
40
|
}
|
|
37
41
|
/**
|
|
@@ -47,7 +51,7 @@ interface MarkdownEditorBlockProps extends Omit<React.HTMLAttributes<HTMLDivElem
|
|
|
47
51
|
* running thinking finishes on the version it started with — editing without
|
|
48
52
|
* seeing which version you are on is how two people overwrite each other.
|
|
49
53
|
*/
|
|
50
|
-
declare function MarkdownEditorBlock({ value, onChange, readOnly, version, className, ...props }: MarkdownEditorBlockProps): React.JSX.Element;
|
|
54
|
+
declare function MarkdownEditorBlock({ value, onChange, readOnly, version, placeholder, className, ...props }: MarkdownEditorBlockProps): React.JSX.Element;
|
|
51
55
|
|
|
52
56
|
interface UseCodeMirrorOptions {
|
|
53
57
|
value: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
import { Extension } from '@codemirror/state';
|
|
3
3
|
import { EditorView } from '@codemirror/view';
|
|
4
4
|
|
|
5
|
-
type CodeLanguage = "python" | "shell" | "javascript" | "json" | "cypher" | "plain";
|
|
5
|
+
type CodeLanguage = "python" | "shell" | "javascript" | "json" | "cypher" | "yaml" | "plain";
|
|
6
6
|
interface CodeBlockProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
|
|
7
7
|
value: string;
|
|
8
8
|
language?: CodeLanguage;
|
|
@@ -32,6 +32,10 @@ interface MarkdownEditorBlockProps extends Omit<React.HTMLAttributes<HTMLDivElem
|
|
|
32
32
|
readOnly?: boolean;
|
|
33
33
|
/** A version marker shown in the corner — `v5`, `v7 · editing`. */
|
|
34
34
|
version?: React.ReactNode;
|
|
35
|
+
/**
|
|
36
|
+
* Drawn in the empty editor, never stored — what the reader gets when
|
|
37
|
+
* nothing is written, such as an agent's default voice.
|
|
38
|
+
*/
|
|
35
39
|
placeholder?: string;
|
|
36
40
|
}
|
|
37
41
|
/**
|
|
@@ -47,7 +51,7 @@ interface MarkdownEditorBlockProps extends Omit<React.HTMLAttributes<HTMLDivElem
|
|
|
47
51
|
* running thinking finishes on the version it started with — editing without
|
|
48
52
|
* seeing which version you are on is how two people overwrite each other.
|
|
49
53
|
*/
|
|
50
|
-
declare function MarkdownEditorBlock({ value, onChange, readOnly, version, className, ...props }: MarkdownEditorBlockProps): React.JSX.Element;
|
|
54
|
+
declare function MarkdownEditorBlock({ value, onChange, readOnly, version, placeholder, className, ...props }: MarkdownEditorBlockProps): React.JSX.Element;
|
|
51
55
|
|
|
52
56
|
interface UseCodeMirrorOptions {
|
|
53
57
|
value: string;
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { EditorView, lineNumbers, keymap } from '@codemirror/view';
|
|
2
|
+
import { EditorView, lineNumbers, keymap, placeholder } from '@codemirror/view';
|
|
3
3
|
import { Compartment, EditorState } from '@codemirror/state';
|
|
4
4
|
import { syntaxHighlighting, defaultHighlightStyle, StreamLanguage } from '@codemirror/language';
|
|
5
5
|
import { python } from '@codemirror/legacy-modes/mode/python';
|
|
6
6
|
import { shell } from '@codemirror/legacy-modes/mode/shell';
|
|
7
7
|
import { json, javascript } from '@codemirror/legacy-modes/mode/javascript';
|
|
8
8
|
import { cypher } from '@codemirror/legacy-modes/mode/cypher';
|
|
9
|
+
import { yaml } from '@codemirror/legacy-modes/mode/yaml';
|
|
9
10
|
import { cn } from '@invana/ui';
|
|
10
11
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
11
12
|
import { history, defaultKeymap, historyKeymap } from '@codemirror/commands';
|
|
@@ -23,7 +24,9 @@ function useCodeMirror({
|
|
|
23
24
|
onChangeRef.current = onChange;
|
|
24
25
|
}, [onChange]);
|
|
25
26
|
const extensionsRef = React.useRef(extensions);
|
|
26
|
-
|
|
27
|
+
React.useLayoutEffect(() => {
|
|
28
|
+
extensionsRef.current = extensions;
|
|
29
|
+
}, [extensions]);
|
|
27
30
|
const compartment = React.useRef(new Compartment());
|
|
28
31
|
React.useEffect(() => {
|
|
29
32
|
if (!host.current) return;
|
|
@@ -65,6 +68,7 @@ var MODES = {
|
|
|
65
68
|
javascript: () => StreamLanguage.define(javascript),
|
|
66
69
|
json: () => StreamLanguage.define(json),
|
|
67
70
|
cypher: () => StreamLanguage.define(cypher),
|
|
71
|
+
yaml: () => StreamLanguage.define(yaml),
|
|
68
72
|
plain: () => null
|
|
69
73
|
};
|
|
70
74
|
function CodeBlock({
|
|
@@ -114,6 +118,7 @@ function MarkdownEditorBlock({
|
|
|
114
118
|
onChange,
|
|
115
119
|
readOnly,
|
|
116
120
|
version,
|
|
121
|
+
placeholder: placeholder$1,
|
|
117
122
|
className,
|
|
118
123
|
...props
|
|
119
124
|
}) {
|
|
@@ -126,9 +131,10 @@ function MarkdownEditorBlock({
|
|
|
126
131
|
EditorView.lineWrapping,
|
|
127
132
|
history(),
|
|
128
133
|
keymap.of([...defaultKeymap, ...historyKeymap]),
|
|
129
|
-
EditorView.editable.of(!readOnly)
|
|
134
|
+
EditorView.editable.of(!readOnly),
|
|
135
|
+
...placeholder$1 ? [placeholder(placeholder$1)] : []
|
|
130
136
|
],
|
|
131
|
-
[readOnly]
|
|
137
|
+
[readOnly, placeholder$1]
|
|
132
138
|
);
|
|
133
139
|
const { ref } = useCodeMirror({ value, extensions, onChange });
|
|
134
140
|
return /* @__PURE__ */ jsxs("div", { className: cn("relative border border-border bg-muted/40", className), ...props, children: [
|
|
@@ -139,7 +145,12 @@ function MarkdownEditorBlock({
|
|
|
139
145
|
ref,
|
|
140
146
|
className: cn(
|
|
141
147
|
"[&_.cm-editor]:bg-transparent [&_.cm-content]:font-mono [&_.cm-content]:text-sm",
|
|
142
|
-
"[&_.cm-focused]:outline-none [&_.cm-cursor]:border-foreground"
|
|
148
|
+
"[&_.cm-focused]:outline-none [&_.cm-cursor]:border-foreground",
|
|
149
|
+
"[&_.cm-placeholder]:text-muted-foreground",
|
|
150
|
+
// Room for the version marker, so it never sits over the first line.
|
|
151
|
+
// On the host, not `.cm-content`: CodeMirror's own theme sets that
|
|
152
|
+
// padding and wins.
|
|
153
|
+
version != null && "pr-12"
|
|
143
154
|
)
|
|
144
155
|
}
|
|
145
156
|
)
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/use-code-mirror.ts","../src/code-block.tsx","../src/markdown-editor-block.tsx"],"names":["React2","EditorView","EditorState","React3","cn","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import * as React from \"react\"\nimport { Compartment, EditorState, type Extension } from \"@codemirror/state\"\nimport { EditorView } from \"@codemirror/view\"\n\nexport interface UseCodeMirrorOptions {\n value: string\n extensions: Extension[]\n onChange?: (value: string) => void\n}\n\n/**\n * Mounts a CodeMirror view into a ref'd element and keeps it in sync.\n *\n * The whole reason this is a hook rather than a component: CodeMirror owns its\n * own DOM, so React must not re-render into it. Value changes are pushed as\n * transactions, and only when they actually differ — writing the view's own\n * output back into it would move the cursor to the end on every keystroke.\n */\nexport function useCodeMirror({\n value,\n extensions,\n onChange,\n}: UseCodeMirrorOptions) {\n const host = React.useRef<HTMLDivElement | null>(null)\n const view = React.useRef<EditorView | null>(null)\n\n // Keep the latest onChange without tearing down the view when it changes\n // identity — a caller passing an inline arrow would otherwise remount the\n // editor on every render.\n const onChangeRef = React.useRef(onChange)\n React.useEffect(() => {\n onChangeRef.current = onChange\n }, [onChange])\n\n // The view is created once and reconfigured in place. Keying the effect on\n // `extensions` identity looked simpler, but a caller passing an inline array\n // would then rebuild the editor on every render — losing the cursor, the\n // selection and focus mid-keystroke. \"Callers are expected to memoise\" is a\n // trap, not a contract, so the hook does not depend on it.\n const extensionsRef = React.useRef(extensions)\n extensionsRef.current = extensions\n\n const compartment = React.useRef(new Compartment())\n\n React.useEffect(() => {\n if (!host.current) return\n const v = new EditorView({\n state: EditorState.create({\n doc: value,\n extensions: [\n compartment.current.of(extensionsRef.current),\n EditorView.updateListener.of((u) => {\n if (u.docChanged) onChangeRef.current?.(u.state.doc.toString())\n }),\n ],\n }),\n parent: host.current,\n })\n view.current = v\n return () => {\n v.destroy()\n view.current = null\n }\n // Mount once. Configuration changes go through the compartment below.\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Swap configuration without touching the document — this is what a\n // Compartment is for, and it is why toggling `readOnly` or a language keeps\n // the caret where it was.\n React.useEffect(() => {\n const v = view.current\n if (!v) return\n v.dispatch({ effects: compartment.current.reconfigure(extensions) })\n }, [extensions])\n\n React.useEffect(() => {\n const v = view.current\n if (!v) return\n const current = v.state.doc.toString()\n if (current === value) return\n v.dispatch({ changes: { from: 0, to: current.length, insert: value } })\n }, [value])\n\n return { ref: host, view }\n}\n","import * as React from \"react\"\nimport { EditorView, lineNumbers } from \"@codemirror/view\"\nimport { EditorState } from \"@codemirror/state\"\nimport {\n StreamLanguage,\n syntaxHighlighting,\n defaultHighlightStyle,\n} from \"@codemirror/language\"\nimport { python } from \"@codemirror/legacy-modes/mode/python\"\nimport { shell } from \"@codemirror/legacy-modes/mode/shell\"\nimport { javascript, json } from \"@codemirror/legacy-modes/mode/javascript\"\nimport { cypher } from \"@codemirror/legacy-modes/mode/cypher\"\nimport { cn } from \"@invana/ui\"\n\nimport { useCodeMirror } from \"./use-code-mirror\"\n\nexport type CodeLanguage =\n | \"python\"\n | \"shell\"\n | \"javascript\"\n | \"json\"\n | \"cypher\"\n | \"plain\"\n\nexport interface CodeBlockProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n value: string\n language?: CodeLanguage\n /** Line numbers are off by default — a five-line snippet does not need a gutter. */\n showLineNumbers?: boolean\n /**\n * Cap the block's height in px and let it scroll inside that, for a document\n * whose length is not the point — a run's `result.json`, an exported model.\n *\n * Unset, the block is as tall as its content, which is right for a snippet\n * and wrong for a record that grows as a run proceeds.\n */\n maxHeight?: number\n}\n\nconst MODES = {\n python: () => StreamLanguage.define(python),\n shell: () => StreamLanguage.define(shell),\n javascript: () => StreamLanguage.define(javascript),\n json: () => StreamLanguage.define(json),\n cypher: () => StreamLanguage.define(cypher),\n plain: () => null,\n}\n\n/**\n * Code, shown as it is — read-only.\n *\n * The DAG in a hand-off, a query in a diagnosis, an exported model. It is not a\n * disabled editor: `EditorState.readOnly` plus a non-editable view means there\n * is no cursor to place and no illusion that typing would do something.\n */\nexport function CodeBlock({\n value,\n language = \"plain\",\n showLineNumbers,\n maxHeight,\n className,\n style,\n ...props\n}: CodeBlockProps) {\n const extensions = React.useMemo(() => {\n const mode = MODES[language]()\n return [\n // StreamLanguage only produces tokens; without a highlight style nothing\n // paints them and the block renders as flat text.\n syntaxHighlighting(defaultHighlightStyle, { fallback: true }),\n EditorView.editable.of(false),\n EditorState.readOnly.of(true),\n EditorView.lineWrapping,\n ...(showLineNumbers ? [lineNumbers()] : []),\n ...(mode ? [mode] : []),\n // The scroller lives inside CodeMirror rather than on the wrapper, so the\n // editor knows its own viewport and does not render every line of a long\n // document into a box that then clips them.\n ...(maxHeight != null\n ? [EditorView.theme({ \"&\": { maxHeight: `${maxHeight}px` } })]\n : []),\n ]\n }, [language, showLineNumbers, maxHeight])\n\n const { ref } = useCodeMirror({ value, extensions })\n\n return (\n <div\n ref={ref}\n className={cn(\n \"overflow-hidden border border-border bg-muted/40 [&_.cm-editor]:bg-transparent\",\n \"[&_.cm-content]:font-mono [&_.cm-content]:text-sm [&_.cm-focused]:outline-none\",\n \"[&_.cm-gutters]:border-none [&_.cm-gutters]:bg-transparent [&_.cm-gutters]:text-muted-foreground\",\n className,\n )}\n style={style}\n {...props}\n />\n )\n}\n","import * as React from \"react\"\nimport { EditorView } from \"@codemirror/view\"\nimport { history, defaultKeymap, historyKeymap } from \"@codemirror/commands\"\nimport { keymap } from \"@codemirror/view\"\nimport { cn } from \"@invana/ui\"\n\nimport { useCodeMirror } from \"./use-code-mirror\"\n\nexport interface MarkdownEditorBlockProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\" | \"children\"> {\n value: string\n onChange?: (value: string) => void\n readOnly?: boolean\n /** A version marker shown in the corner — `v5`, `v7 · editing`. */\n version?: React.ReactNode\n placeholder?: string\n}\n\n/**\n * The prose a person writes for an agent to read — graph rules, a skill's\n * content, an acceptance note.\n *\n * Plain markdown in a monospace field, not a rich-text editor. What is typed\n * here is offered verbatim in a model's prompt, so what you see has to be\n * exactly what the agent gets; a WYSIWYG layer would put a translation step\n * between the author and the thing that runs.\n *\n * `version` sits in the corner because these documents are versioned and a\n * running thinking finishes on the version it started with — editing without\n * seeing which version you are on is how two people overwrite each other.\n */\nexport function MarkdownEditorBlock({\n value,\n onChange,\n readOnly,\n version,\n className,\n ...props\n}: MarkdownEditorBlockProps) {\n const extensions = React.useMemo(\n () => [\n // No markdown syntax mode, deliberately. What is typed here is offered\n // verbatim in a model's prompt, and highlighting it would be decoration\n // on text whose whole contract is that it is plain. It also keeps this\n // package's dependencies to the CodeMirror set the product already ships.\n EditorView.lineWrapping,\n history(),\n keymap.of([...defaultKeymap, ...historyKeymap]),\n EditorView.editable.of(!readOnly),\n ],\n [readOnly],\n )\n\n const { ref } = useCodeMirror({ value, extensions, onChange })\n\n return (\n <div className={cn(\"relative border border-border bg-muted/40\", className)} {...props}>\n {version != null ? (\n <span className=\"absolute right-1 top-1 z-10 border border-border bg-card px-1 text-sm text-muted-foreground\">\n {version}\n </span>\n ) : null}\n <div\n ref={ref}\n className={cn(\n \"[&_.cm-editor]:bg-transparent [&_.cm-content]:font-mono [&_.cm-content]:text-sm\",\n \"[&_.cm-focused]:outline-none [&_.cm-cursor]:border-foreground\",\n )}\n />\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/use-code-mirror.ts","../src/code-block.tsx","../src/markdown-editor-block.tsx"],"names":["React2","EditorView","EditorState","placeholder","React3","cmPlaceholder","cn","jsx"],"mappings":";;;;;;;;;;;;;;AAkBO,SAAS,aAAA,CAAc;AAAA,EAC5B,KAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF,CAAA,EAAyB;AACvB,EAAA,MAAM,IAAA,GAAa,aAA8B,IAAI,CAAA;AACrD,EAAA,MAAM,IAAA,GAAa,aAA0B,IAAI,CAAA;AAKjD,EAAA,MAAM,WAAA,GAAoB,aAAO,QAAQ,CAAA;AACzC,EAAM,gBAAU,MAAM;AACpB,IAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AAAA,EACxB,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAOb,EAAA,MAAM,aAAA,GAAsB,aAAO,UAAU,CAAA;AAE7C,EAAM,sBAAgB,MAAM;AAC1B,IAAA,aAAA,CAAc,OAAA,GAAU,UAAA;AAAA,EAC1B,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,MAAM,WAAA,GAAoB,KAAA,CAAA,MAAA,CAAO,IAAI,WAAA,EAAa,CAAA;AAElD,EAAM,gBAAU,MAAM;AACpB,IAAA,IAAI,CAAC,KAAK,OAAA,EAAS;AACnB,IAAA,MAAM,CAAA,GAAI,IAAI,UAAA,CAAW;AAAA,MACvB,KAAA,EAAO,YAAY,MAAA,CAAO;AAAA,QACxB,GAAA,EAAK,KAAA;AAAA,QACL,UAAA,EAAY;AAAA,UACV,WAAA,CAAY,OAAA,CAAQ,EAAA,CAAG,aAAA,CAAc,OAAO,CAAA;AAAA,UAC5C,UAAA,CAAW,cAAA,CAAe,EAAA,CAAG,CAAC,CAAA,KAAM;AAClC,YAAA,IAAI,CAAA,CAAE,YAAY,WAAA,CAAY,OAAA,GAAU,EAAE,KAAA,CAAM,GAAA,CAAI,UAAU,CAAA;AAAA,UAChE,CAAC;AAAA;AACH,OACD,CAAA;AAAA,MACD,QAAQ,IAAA,CAAK;AAAA,KACd,CAAA;AACD,IAAA,IAAA,CAAK,OAAA,GAAU,CAAA;AACf,IAAA,OAAO,MAAM;AACX,MAAA,CAAA,CAAE,OAAA,EAAQ;AACV,MAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AAAA,IACjB,CAAA;AAAA,EAEF,CAAA,EAAG,EAAE,CAAA;AAKL,EAAM,gBAAU,MAAM;AACpB,IAAA,MAAM,IAAI,IAAA,CAAK,OAAA;AACf,IAAA,IAAI,CAAC,CAAA,EAAG;AACR,IAAA,CAAA,CAAE,QAAA,CAAS,EAAE,OAAA,EAAS,WAAA,CAAY,QAAQ,WAAA,CAAY,UAAU,GAAG,CAAA;AAAA,EACrE,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAM,gBAAU,MAAM;AACpB,IAAA,MAAM,IAAI,IAAA,CAAK,OAAA;AACf,IAAA,IAAI,CAAC,CAAA,EAAG;AACR,IAAA,MAAM,OAAA,GAAU,CAAA,CAAE,KAAA,CAAM,GAAA,CAAI,QAAA,EAAS;AACrC,IAAA,IAAI,YAAY,KAAA,EAAO;AACvB,IAAA,CAAA,CAAE,QAAA,CAAS,EAAE,OAAA,EAAS,EAAE,IAAA,EAAM,CAAA,EAAG,EAAA,EAAI,OAAA,CAAQ,MAAA,EAAQ,MAAA,EAAQ,KAAA,EAAM,EAAG,CAAA;AAAA,EACxE,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,OAAO,EAAE,GAAA,EAAK,IAAA,EAAM,IAAA,EAAK;AAC3B;AC7CA,IAAM,KAAA,GAAQ;AAAA,EACZ,MAAA,EAAQ,MAAM,cAAA,CAAe,MAAA,CAAO,MAAM,CAAA;AAAA,EAC1C,KAAA,EAAO,MAAM,cAAA,CAAe,MAAA,CAAO,KAAK,CAAA;AAAA,EACxC,UAAA,EAAY,MAAM,cAAA,CAAe,MAAA,CAAO,UAAU,CAAA;AAAA,EAClD,IAAA,EAAM,MAAM,cAAA,CAAe,MAAA,CAAO,IAAI,CAAA;AAAA,EACtC,MAAA,EAAQ,MAAM,cAAA,CAAe,MAAA,CAAO,MAAM,CAAA;AAAA,EAC1C,IAAA,EAAM,MAAM,cAAA,CAAe,MAAA,CAAO,IAAI,CAAA;AAAA,EACtC,OAAO,MAAM;AACf,CAAA;AASO,SAAS,SAAA,CAAU;AAAA,EACxB,KAAA;AAAA,EACA,QAAA,GAAW,OAAA;AAAA,EACX,eAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAmB;AACjB,EAAA,MAAM,UAAA,GAAmBA,cAAQ,MAAM;AACrC,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,QAAQ,CAAA,EAAE;AAC7B,IAAA,OAAO;AAAA;AAAA;AAAA,MAGL,kBAAA,CAAmB,qBAAA,EAAuB,EAAE,QAAA,EAAU,MAAM,CAAA;AAAA,MAC5DC,UAAAA,CAAW,QAAA,CAAS,EAAA,CAAG,KAAK,CAAA;AAAA,MAC5BC,WAAAA,CAAY,QAAA,CAAS,EAAA,CAAG,IAAI,CAAA;AAAA,MAC5BD,UAAAA,CAAW,YAAA;AAAA,MACX,GAAI,eAAA,GAAkB,CAAC,WAAA,EAAa,IAAI,EAAC;AAAA,MACzC,GAAI,IAAA,GAAO,CAAC,IAAI,IAAI,EAAC;AAAA;AAAA;AAAA;AAAA,MAIrB,GAAI,SAAA,IAAa,IAAA,GACb,CAACA,UAAAA,CAAW,MAAM,EAAE,GAAA,EAAK,EAAE,SAAA,EAAW,GAAG,SAAS,CAAA,EAAA,CAAA,IAAQ,CAAC,IAC3D;AAAC,KACP;AAAA,EACF,CAAA,EAAG,CAAC,QAAA,EAAU,eAAA,EAAiB,SAAS,CAAC,CAAA;AAEzC,EAAA,MAAM,EAAE,GAAA,EAAI,GAAI,cAAc,EAAE,KAAA,EAAO,YAAY,CAAA;AAEnD,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACT,gFAAA;AAAA,QACA,gFAAA;AAAA,QACA,kGAAA;AAAA,QACA;AAAA,OACF;AAAA,MACA,KAAA;AAAA,MACC,GAAG;AAAA;AAAA,GACN;AAEJ;ACpEO,SAAS,mBAAA,CAAoB;AAAA,EAClC,KAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,eACAE,aAAA;AAAA,EACA,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAA6B;AAC3B,EAAA,MAAM,UAAA,GAAmBC,KAAA,CAAA,OAAA;AAAA,IACvB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,MAKJH,UAAAA,CAAW,YAAA;AAAA,MACX,OAAA,EAAQ;AAAA,MACR,OAAO,EAAA,CAAG,CAAC,GAAG,aAAA,EAAe,GAAG,aAAa,CAAC,CAAA;AAAA,MAC9CA,UAAAA,CAAW,QAAA,CAAS,EAAA,CAAG,CAAC,QAAQ,CAAA;AAAA,MAChC,GAAIE,aAAA,GAAc,CAACE,YAAcF,aAAW,CAAC,IAAI;AAAC,KACpD;AAAA,IACA,CAAC,UAAUA,aAAW;AAAA,GACxB;AAEA,EAAA,MAAM,EAAE,KAAI,GAAI,aAAA,CAAc,EAAE,KAAA,EAAO,UAAA,EAAY,UAAU,CAAA;AAE7D,EAAA,uBACE,IAAA,CAAC,SAAI,SAAA,EAAWG,EAAAA,CAAG,6CAA6C,SAAS,CAAA,EAAI,GAAG,KAAA,EAC7E,QAAA,EAAA;AAAA,IAAA,OAAA,IAAW,uBACVC,GAAAA,CAAC,UAAK,SAAA,EAAU,6FAAA,EACb,mBACH,CAAA,GACE,IAAA;AAAA,oBACJA,GAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAWD,EAAAA;AAAA,UACT,iFAAA;AAAA,UACA,+DAAA;AAAA,UACA,2CAAA;AAAA;AAAA;AAAA;AAAA,UAIA,WAAW,IAAA,IAAQ;AAAA;AACrB;AAAA;AACF,GAAA,EACF,CAAA;AAEJ","file":"index.js","sourcesContent":["import * as React from \"react\"\nimport { Compartment, EditorState, type Extension } from \"@codemirror/state\"\nimport { EditorView } from \"@codemirror/view\"\n\nexport interface UseCodeMirrorOptions {\n value: string\n extensions: Extension[]\n onChange?: (value: string) => void\n}\n\n/**\n * Mounts a CodeMirror view into a ref'd element and keeps it in sync.\n *\n * The whole reason this is a hook rather than a component: CodeMirror owns its\n * own DOM, so React must not re-render into it. Value changes are pushed as\n * transactions, and only when they actually differ — writing the view's own\n * output back into it would move the cursor to the end on every keystroke.\n */\nexport function useCodeMirror({\n value,\n extensions,\n onChange,\n}: UseCodeMirrorOptions) {\n const host = React.useRef<HTMLDivElement | null>(null)\n const view = React.useRef<EditorView | null>(null)\n\n // Keep the latest onChange without tearing down the view when it changes\n // identity — a caller passing an inline arrow would otherwise remount the\n // editor on every render.\n const onChangeRef = React.useRef(onChange)\n React.useEffect(() => {\n onChangeRef.current = onChange\n }, [onChange])\n\n // The view is created once and reconfigured in place. Keying the effect on\n // `extensions` identity looked simpler, but a caller passing an inline array\n // would then rebuild the editor on every render — losing the cursor, the\n // selection and focus mid-keystroke. \"Callers are expected to memoise\" is a\n // trap, not a contract, so the hook does not depend on it.\n const extensionsRef = React.useRef(extensions)\n // Synced after commit, before the effects below read it — never mid-render.\n React.useLayoutEffect(() => {\n extensionsRef.current = extensions\n }, [extensions])\n\n const compartment = React.useRef(new Compartment())\n\n React.useEffect(() => {\n if (!host.current) return\n const v = new EditorView({\n state: EditorState.create({\n doc: value,\n extensions: [\n compartment.current.of(extensionsRef.current),\n EditorView.updateListener.of((u) => {\n if (u.docChanged) onChangeRef.current?.(u.state.doc.toString())\n }),\n ],\n }),\n parent: host.current,\n })\n view.current = v\n return () => {\n v.destroy()\n view.current = null\n }\n // Mount once. Configuration changes go through the compartment below.\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Swap configuration without touching the document — this is what a\n // Compartment is for, and it is why toggling `readOnly` or a language keeps\n // the caret where it was.\n React.useEffect(() => {\n const v = view.current\n if (!v) return\n v.dispatch({ effects: compartment.current.reconfigure(extensions) })\n }, [extensions])\n\n React.useEffect(() => {\n const v = view.current\n if (!v) return\n const current = v.state.doc.toString()\n if (current === value) return\n v.dispatch({ changes: { from: 0, to: current.length, insert: value } })\n }, [value])\n\n return { ref: host, view }\n}\n","import * as React from \"react\"\nimport { EditorView, lineNumbers } from \"@codemirror/view\"\nimport { EditorState } from \"@codemirror/state\"\nimport {\n StreamLanguage,\n syntaxHighlighting,\n defaultHighlightStyle,\n} from \"@codemirror/language\"\nimport { python } from \"@codemirror/legacy-modes/mode/python\"\nimport { shell } from \"@codemirror/legacy-modes/mode/shell\"\nimport { javascript, json } from \"@codemirror/legacy-modes/mode/javascript\"\nimport { cypher } from \"@codemirror/legacy-modes/mode/cypher\"\nimport { yaml } from \"@codemirror/legacy-modes/mode/yaml\"\nimport { cn } from \"@invana/ui\"\n\nimport { useCodeMirror } from \"./use-code-mirror\"\n\nexport type CodeLanguage =\n | \"python\"\n | \"shell\"\n | \"javascript\"\n | \"json\"\n | \"cypher\"\n | \"yaml\"\n | \"plain\"\n\nexport interface CodeBlockProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n value: string\n language?: CodeLanguage\n /** Line numbers are off by default — a five-line snippet does not need a gutter. */\n showLineNumbers?: boolean\n /**\n * Cap the block's height in px and let it scroll inside that, for a document\n * whose length is not the point — a run's `result.json`, an exported model.\n *\n * Unset, the block is as tall as its content, which is right for a snippet\n * and wrong for a record that grows as a run proceeds.\n */\n maxHeight?: number\n}\n\nconst MODES = {\n python: () => StreamLanguage.define(python),\n shell: () => StreamLanguage.define(shell),\n javascript: () => StreamLanguage.define(javascript),\n json: () => StreamLanguage.define(json),\n cypher: () => StreamLanguage.define(cypher),\n yaml: () => StreamLanguage.define(yaml),\n plain: () => null,\n}\n\n/**\n * Code, shown as it is — read-only.\n *\n * The DAG in a hand-off, a query in a diagnosis, an exported model. It is not a\n * disabled editor: `EditorState.readOnly` plus a non-editable view means there\n * is no cursor to place and no illusion that typing would do something.\n */\nexport function CodeBlock({\n value,\n language = \"plain\",\n showLineNumbers,\n maxHeight,\n className,\n style,\n ...props\n}: CodeBlockProps) {\n const extensions = React.useMemo(() => {\n const mode = MODES[language]()\n return [\n // StreamLanguage only produces tokens; without a highlight style nothing\n // paints them and the block renders as flat text.\n syntaxHighlighting(defaultHighlightStyle, { fallback: true }),\n EditorView.editable.of(false),\n EditorState.readOnly.of(true),\n EditorView.lineWrapping,\n ...(showLineNumbers ? [lineNumbers()] : []),\n ...(mode ? [mode] : []),\n // The scroller lives inside CodeMirror rather than on the wrapper, so the\n // editor knows its own viewport and does not render every line of a long\n // document into a box that then clips them.\n ...(maxHeight != null\n ? [EditorView.theme({ \"&\": { maxHeight: `${maxHeight}px` } })]\n : []),\n ]\n }, [language, showLineNumbers, maxHeight])\n\n const { ref } = useCodeMirror({ value, extensions })\n\n return (\n <div\n ref={ref}\n className={cn(\n \"overflow-hidden border border-border bg-muted/40 [&_.cm-editor]:bg-transparent\",\n \"[&_.cm-content]:font-mono [&_.cm-content]:text-sm [&_.cm-focused]:outline-none\",\n \"[&_.cm-gutters]:border-none [&_.cm-gutters]:bg-transparent [&_.cm-gutters]:text-muted-foreground\",\n className,\n )}\n style={style}\n {...props}\n />\n )\n}\n","import * as React from \"react\"\nimport { EditorView } from \"@codemirror/view\"\nimport { history, defaultKeymap, historyKeymap } from \"@codemirror/commands\"\nimport { keymap, placeholder as cmPlaceholder } from \"@codemirror/view\"\nimport { cn } from \"@invana/ui\"\n\nimport { useCodeMirror } from \"./use-code-mirror\"\n\nexport interface MarkdownEditorBlockProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\" | \"children\"> {\n value: string\n onChange?: (value: string) => void\n readOnly?: boolean\n /** A version marker shown in the corner — `v5`, `v7 · editing`. */\n version?: React.ReactNode\n /**\n * Drawn in the empty editor, never stored — what the reader gets when\n * nothing is written, such as an agent's default voice.\n */\n placeholder?: string\n}\n\n/**\n * The prose a person writes for an agent to read — graph rules, a skill's\n * content, an acceptance note.\n *\n * Plain markdown in a monospace field, not a rich-text editor. What is typed\n * here is offered verbatim in a model's prompt, so what you see has to be\n * exactly what the agent gets; a WYSIWYG layer would put a translation step\n * between the author and the thing that runs.\n *\n * `version` sits in the corner because these documents are versioned and a\n * running thinking finishes on the version it started with — editing without\n * seeing which version you are on is how two people overwrite each other.\n */\nexport function MarkdownEditorBlock({\n value,\n onChange,\n readOnly,\n version,\n placeholder,\n className,\n ...props\n}: MarkdownEditorBlockProps) {\n const extensions = React.useMemo(\n () => [\n // No markdown syntax mode, deliberately. What is typed here is offered\n // verbatim in a model's prompt, and highlighting it would be decoration\n // on text whose whole contract is that it is plain. It also keeps this\n // package's dependencies to the CodeMirror set the product already ships.\n EditorView.lineWrapping,\n history(),\n keymap.of([...defaultKeymap, ...historyKeymap]),\n EditorView.editable.of(!readOnly),\n ...(placeholder ? [cmPlaceholder(placeholder)] : []),\n ],\n [readOnly, placeholder],\n )\n\n const { ref } = useCodeMirror({ value, extensions, onChange })\n\n return (\n <div className={cn(\"relative border border-border bg-muted/40\", className)} {...props}>\n {version != null ? (\n <span className=\"absolute right-1 top-1 z-10 border border-border bg-card px-1 text-sm text-muted-foreground\">\n {version}\n </span>\n ) : null}\n <div\n ref={ref}\n className={cn(\n \"[&_.cm-editor]:bg-transparent [&_.cm-content]:font-mono [&_.cm-content]:text-sm\",\n \"[&_.cm-focused]:outline-none [&_.cm-cursor]:border-foreground\",\n \"[&_.cm-placeholder]:text-muted-foreground\",\n // Room for the version marker, so it never sits over the first line.\n // On the host, not `.cm-content`: CodeMirror's own theme sets that\n // padding and wins.\n version != null && \"pr-12\",\n )}\n />\n </div>\n )\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@invana/editor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.33",
|
|
4
4
|
"description": "Invana Design System - Code and markdown editing surfaces (CodeMirror 6)",
|
|
5
5
|
"homepage": "https://github.com/invana/design-kit#readme",
|
|
6
6
|
"type": "module",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"react": "^18 || ^19",
|
|
42
42
|
"react-dom": "^18 || ^19",
|
|
43
|
-
"@invana/
|
|
44
|
-
"@invana/
|
|
43
|
+
"@invana/ui": "0.0.33",
|
|
44
|
+
"@invana/styling": "0.0.33"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@codemirror/commands": "^6.10.3",
|
|
@@ -57,12 +57,12 @@
|
|
|
57
57
|
"react-dom": "^19.2.7",
|
|
58
58
|
"tsup": "^8.5.1",
|
|
59
59
|
"typescript": "~5.9.3",
|
|
60
|
-
"@invana/styling": "0.0.
|
|
61
|
-
"@invana/ui": "0.0.
|
|
60
|
+
"@invana/styling": "0.0.33",
|
|
61
|
+
"@invana/ui": "0.0.33"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"build": "tsup",
|
|
65
65
|
"lint": "eslint .",
|
|
66
|
-
"type-check": "tsc --noEmit"
|
|
66
|
+
"type-check": "tsc --noEmit -p tsconfig.lib.json"
|
|
67
67
|
}
|
|
68
68
|
}
|