@aleph-alpha/chat-kit 6.2.0 → 6.3.0
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/{CkHistory.vue_vue_type_script_setup_true_lang-DPCpIxWc.js → CkHistory.vue_vue_type_script_setup_true_lang-gV-lp0ZB.js} +345 -164
- package/dist/chat-kit.css +111 -28
- package/dist/code-palette-DAJUTnR-.js +21598 -0
- package/dist/components/base/CkCodeEditor/CkCodeEditor.stories.d.ts +43 -0
- package/dist/components/base/CkCodeEditor/CkCodeEditor.stories.d.ts.map +1 -0
- package/dist/components/base/CkCodeEditor/CkCodeEditor.vue.d.ts +39 -0
- package/dist/components/base/CkCodeEditor/CkCodeEditor.vue.d.ts.map +1 -0
- package/dist/components/base/CkCodeEditor/createCodeMirror.d.ts +44 -0
- package/dist/components/base/CkCodeEditor/createCodeMirror.d.ts.map +1 -0
- package/dist/components/base/CkCodeEditor/index.d.ts +3 -0
- package/dist/components/base/CkCodeEditor/index.d.ts.map +1 -0
- package/dist/components/base/CkCodeEditor/types.d.ts +129 -0
- package/dist/components/base/CkCodeEditor/types.d.ts.map +1 -0
- package/dist/components/base/CkMilkdownEditor/CkMilkdownEditor.stories.d.ts +6 -0
- package/dist/components/base/CkMilkdownEditor/CkMilkdownEditor.stories.d.ts.map +1 -1
- package/dist/components/base/CkMilkdownEditor/CkMilkdownEditor.vue.d.ts +1 -0
- package/dist/components/base/CkMilkdownEditor/CkMilkdownEditor.vue.d.ts.map +1 -1
- package/dist/components/base/CkMilkdownEditor/createCrepeEditor.d.ts +1 -0
- package/dist/components/base/CkMilkdownEditor/createCrepeEditor.d.ts.map +1 -1
- package/dist/components/base/CkMilkdownEditor/types.d.ts +8 -0
- package/dist/components/base/CkMilkdownEditor/types.d.ts.map +1 -1
- package/dist/components/base/index.d.ts +2 -0
- package/dist/components/base/index.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +27 -26
- package/dist/createCodeMirror-BkV6FEPn.js +101 -0
- package/dist/{createCrepeEditor-C9seqnzm.js → createCrepeEditor-Ci5FHHIV.js} +1433 -24739
- package/dist/handlers/index.js +1 -1
- package/dist/handlers/useChatKitLabels.d.ts +6 -0
- package/dist/handlers/useChatKitLabels.d.ts.map +1 -1
- package/dist/{components/base/CkMilkdownEditor/codeMirrorTheme.d.ts → helpers/codemirror/theme.d.ts} +1 -1
- package/dist/helpers/codemirror/theme.d.ts.map +1 -0
- package/dist/{index-DzpUblMx.js → index-B2tew8uf.js} +3 -3
- package/dist/{index-DLZcs4H8.js → index-BLVAx_f8.js} +2 -2
- package/dist/{index-CHtY7ztn.js → index-BUCqcTPA.js} +2 -2
- package/dist/{index-8mYiDHFC.js → index-BhvbqYsK.js} +2 -2
- package/dist/{index-B0nlcqqF.js → index-BoUJGvvQ.js} +2 -2
- package/dist/{index-U4LYutvz.js → index-BtY1uxn6.js} +3 -2
- package/dist/{index-C5UC1wP7.js → index-BvG6l44n.js} +3 -2
- package/dist/{index-D85elIIr.js → index-CB6xZVsf.js} +2 -2
- package/dist/{index-DbVRgXxM.js → index-CIF93PZF.js} +3 -3
- package/dist/{index-reQfKGuJ.js → index-CN8d3yl1.js} +1 -1
- package/dist/{index-By7WdPbk.js → index-CYk7AB8M.js} +4 -4
- package/dist/{index-DO8xk00f.js → index-Cxs6kPho.js} +3 -3
- package/dist/index-D6ftdiLa.js +1825 -0
- package/dist/{index-DXTRMb09.js → index-DgCcFC3N.js} +3 -3
- package/dist/{index-Bxg0Q_mk.js → index-DhwsowEf.js} +2 -2
- package/dist/{index-C90_LSx6.js → index-Dl24CCB3.js} +4 -4
- package/dist/{index-eZunhxNH.js → index-DqGDenO9.js} +2 -2
- package/dist/{index-DfvAv_Y2.js → index-DufAP1JF.js} +3 -2
- package/dist/{index-VbqLf82s.js → index-DxTzaHLr.js} +4 -4
- package/dist/{index-CFdcEeXx.js → index-MfXW4ZyN.js} +3 -2
- package/dist/{index-6PiPEjJd.js → index-ZHg2R9iD.js} +3 -2
- package/dist/{index-DNxEOsDD.js → index-gYt3E3Cp.js} +2 -2
- package/dist/{index-DINJn4Mn.js → index-w2yzH7vs.js} +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -28
- package/dist/{useChatKitLabels-Bz58tas9.js → useChatKitLabels-CatQSpfL.js} +2 -0
- package/package.json +5 -3
- package/src/components/base/CkCodeEditor/CkCodeEditor.spec.ts +223 -0
- package/src/components/base/CkCodeEditor/CkCodeEditor.stories.ts +311 -0
- package/src/components/base/CkCodeEditor/CkCodeEditor.vue +238 -0
- package/src/components/base/CkCodeEditor/createCodeMirror.ts +188 -0
- package/src/components/base/CkCodeEditor/index.ts +2 -0
- package/src/components/base/CkCodeEditor/types.ts +128 -0
- package/src/components/base/CkMilkdownEditor/CkMilkdownEditor.spec.ts +13 -0
- package/src/components/base/CkMilkdownEditor/CkMilkdownEditor.stories.ts +34 -0
- package/src/components/base/CkMilkdownEditor/CkMilkdownEditor.vue +3 -1
- package/src/components/base/CkMilkdownEditor/createCrepeEditor.ts +2 -1
- package/src/components/base/CkMilkdownEditor/milkdown-theme.css +39 -26
- package/src/components/base/CkMilkdownEditor/types.ts +8 -0
- package/src/components/base/index.ts +6 -0
- package/src/components/index.ts +4 -0
- package/src/handlers/useChatKitLabels.ts +6 -0
- package/src/helpers/codemirror/code-palette.css +49 -0
- package/src/{components/base/CkMilkdownEditor/codeMirrorTheme.ts → helpers/codemirror/theme.ts} +23 -12
- package/src/index.ts +4 -0
- package/dist/components/base/CkMilkdownEditor/codeMirrorTheme.d.ts.map +0 -1
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defaultKeymap,
|
|
3
|
+
history,
|
|
4
|
+
historyKeymap,
|
|
5
|
+
indentWithTab,
|
|
6
|
+
} from '@codemirror/commands';
|
|
7
|
+
import {
|
|
8
|
+
bracketMatching,
|
|
9
|
+
indentOnInput,
|
|
10
|
+
indentUnit,
|
|
11
|
+
type LanguageSupport,
|
|
12
|
+
} from '@codemirror/language';
|
|
13
|
+
import { languages } from '@codemirror/language-data';
|
|
14
|
+
import { Compartment, EditorState, type Extension } from '@codemirror/state';
|
|
15
|
+
import {
|
|
16
|
+
EditorView,
|
|
17
|
+
drawSelection,
|
|
18
|
+
highlightActiveLine,
|
|
19
|
+
highlightSpecialChars,
|
|
20
|
+
keymap,
|
|
21
|
+
lineNumbers,
|
|
22
|
+
placeholder as placeholderExt,
|
|
23
|
+
} from '@codemirror/view';
|
|
24
|
+
import { ckCodeMirrorTheme } from '../../../helpers/codemirror/theme';
|
|
25
|
+
import '../../../helpers/codemirror/code-palette.css';
|
|
26
|
+
|
|
27
|
+
export interface CreateCodeMirrorOptions {
|
|
28
|
+
/** Element the view is mounted into. */
|
|
29
|
+
parent: HTMLElement;
|
|
30
|
+
doc: string;
|
|
31
|
+
language: string;
|
|
32
|
+
placeholder?: string;
|
|
33
|
+
ariaLabel?: string;
|
|
34
|
+
readonly: boolean;
|
|
35
|
+
showLineNumbers: boolean;
|
|
36
|
+
lineWrapping: boolean;
|
|
37
|
+
/** Called synchronously whenever the document changes. */
|
|
38
|
+
onChange: (value: string) => void;
|
|
39
|
+
onFocus: () => void;
|
|
40
|
+
onBlur: () => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Imperative surface `CkCodeEditor` drives; keeps CodeMirror out of the SFC. */
|
|
44
|
+
export interface CodeMirrorHandle {
|
|
45
|
+
view: EditorView;
|
|
46
|
+
getCode: () => string;
|
|
47
|
+
/** Replace the whole document, keeping the cursor where it still fits. */
|
|
48
|
+
setCode: (value: string) => void;
|
|
49
|
+
setLanguage: (language: string) => Promise<void>;
|
|
50
|
+
setReadonly: (readonly: boolean) => void;
|
|
51
|
+
setShowLineNumbers: (show: boolean) => void;
|
|
52
|
+
setLineWrapping: (wrap: boolean) => void;
|
|
53
|
+
focus: () => void;
|
|
54
|
+
destroy: () => void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Plain text, and the value `UiCodeBlock` also treats as "no language". */
|
|
58
|
+
const PLAIN_ALIASES = new Set(['text', 'txt', 'plain', 'plaintext', '']);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Resolve a language id the way a reader would expect: by name, by alias, or by
|
|
62
|
+
* file extension, all case-insensitively. Returns `null` for plain text and for
|
|
63
|
+
* anything CodeMirror does not know, so an unrecognised id degrades to
|
|
64
|
+
* unhighlighted code instead of throwing.
|
|
65
|
+
*/
|
|
66
|
+
export async function resolveLanguage(
|
|
67
|
+
language: string,
|
|
68
|
+
): Promise<LanguageSupport | null> {
|
|
69
|
+
const id = language.trim().toLowerCase();
|
|
70
|
+
if (PLAIN_ALIASES.has(id)) return null;
|
|
71
|
+
|
|
72
|
+
const description = languages.find(
|
|
73
|
+
(candidate) =>
|
|
74
|
+
candidate.name.toLowerCase() === id ||
|
|
75
|
+
candidate.alias.includes(id) ||
|
|
76
|
+
candidate.extensions.includes(id),
|
|
77
|
+
);
|
|
78
|
+
if (!description) return null;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
return await description.load();
|
|
82
|
+
} catch {
|
|
83
|
+
// A grammar that fails to load must not take the editor down with it.
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// `readOnly` rejects edits while keeping the text selectable; `editable`
|
|
89
|
+
// additionally drops `contenteditable`, which hides the caret and keeps the
|
|
90
|
+
// editor out of the tab order.
|
|
91
|
+
function readonlyState(readonly: boolean): Extension {
|
|
92
|
+
return [EditorState.readOnly.of(readonly), EditorView.editable.of(!readonly)];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function baseExtensions(options: CreateCodeMirrorOptions): Extension[] {
|
|
96
|
+
return [
|
|
97
|
+
// Deliberately not CodeMirror's `basicSetup`: it also brings autocomplete,
|
|
98
|
+
// search, folding and bracket closing, none of which suit a snippet field,
|
|
99
|
+
// and its fold gutter would sit next to the line numbers.
|
|
100
|
+
history(),
|
|
101
|
+
drawSelection(),
|
|
102
|
+
highlightSpecialChars(),
|
|
103
|
+
highlightActiveLine(),
|
|
104
|
+
bracketMatching(),
|
|
105
|
+
indentOnInput(),
|
|
106
|
+
indentUnit.of(' '),
|
|
107
|
+
keymap.of([...defaultKeymap, ...historyKeymap, indentWithTab]),
|
|
108
|
+
ckCodeMirrorTheme,
|
|
109
|
+
EditorView.contentAttributes.of({
|
|
110
|
+
role: 'textbox',
|
|
111
|
+
'aria-multiline': 'true',
|
|
112
|
+
...(options.ariaLabel ? { 'aria-label': options.ariaLabel } : {}),
|
|
113
|
+
}),
|
|
114
|
+
EditorView.updateListener.of((update) => {
|
|
115
|
+
if (update.docChanged) options.onChange(update.state.doc.toString());
|
|
116
|
+
if (update.focusChanged) {
|
|
117
|
+
if (update.view.hasFocus) options.onFocus();
|
|
118
|
+
else options.onBlur();
|
|
119
|
+
}
|
|
120
|
+
}),
|
|
121
|
+
...(options.placeholder ? [placeholderExt(options.placeholder)] : []),
|
|
122
|
+
];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Build a CodeMirror view themed like `UiCodeBlock`. Loaded lazily by
|
|
127
|
+
* `CkCodeEditor` so CodeMirror stays out of the package's main chunk.
|
|
128
|
+
*/
|
|
129
|
+
export async function createCodeMirror(
|
|
130
|
+
options: CreateCodeMirrorOptions,
|
|
131
|
+
): Promise<CodeMirrorHandle> {
|
|
132
|
+
// One set per view: a compartment is the key a reconfigure targets, so
|
|
133
|
+
// sharing them across editors would make the instances interfere.
|
|
134
|
+
const languageSlot = new Compartment();
|
|
135
|
+
const readonlySlot = new Compartment();
|
|
136
|
+
const lineNumbersSlot = new Compartment();
|
|
137
|
+
const wrappingSlot = new Compartment();
|
|
138
|
+
|
|
139
|
+
const support = await resolveLanguage(options.language);
|
|
140
|
+
|
|
141
|
+
const view = new EditorView({
|
|
142
|
+
parent: options.parent,
|
|
143
|
+
state: EditorState.create({
|
|
144
|
+
doc: options.doc,
|
|
145
|
+
extensions: [
|
|
146
|
+
...baseExtensions(options),
|
|
147
|
+
languageSlot.of(support ? [support] : []),
|
|
148
|
+
readonlySlot.of(readonlyState(options.readonly)),
|
|
149
|
+
lineNumbersSlot.of(options.showLineNumbers ? lineNumbers() : []),
|
|
150
|
+
wrappingSlot.of(options.lineWrapping ? EditorView.lineWrapping : []),
|
|
151
|
+
],
|
|
152
|
+
}),
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
function reconfigure(slot: Compartment, extension: Extension): void {
|
|
156
|
+
view.dispatch({ effects: slot.reconfigure(extension) });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
view,
|
|
161
|
+
getCode: () => view.state.doc.toString(),
|
|
162
|
+
setCode: (value) => {
|
|
163
|
+
if (value === view.state.doc.toString()) return;
|
|
164
|
+
// Replacing the whole document would otherwise send the cursor home
|
|
165
|
+
// whenever a parent echoes the value back; clamp it to the new length.
|
|
166
|
+
const cursor = Math.min(view.state.selection.main.head, value.length);
|
|
167
|
+
view.dispatch({
|
|
168
|
+
changes: { from: 0, to: view.state.doc.length, insert: value },
|
|
169
|
+
selection: { anchor: cursor },
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
setLanguage: async (language) => {
|
|
173
|
+
const next = await resolveLanguage(language);
|
|
174
|
+
reconfigure(languageSlot, next ? [next] : []);
|
|
175
|
+
},
|
|
176
|
+
setReadonly: (readonly) => {
|
|
177
|
+
reconfigure(readonlySlot, readonlyState(readonly));
|
|
178
|
+
},
|
|
179
|
+
setShowLineNumbers: (show) => {
|
|
180
|
+
reconfigure(lineNumbersSlot, show ? lineNumbers() : []);
|
|
181
|
+
},
|
|
182
|
+
setLineWrapping: (wrap) => {
|
|
183
|
+
reconfigure(wrappingSlot, wrap ? EditorView.lineWrapping : []);
|
|
184
|
+
},
|
|
185
|
+
focus: () => view.focus(),
|
|
186
|
+
destroy: () => view.destroy(),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-document code editor built on [CodeMirror 6](https://codemirror.net/).
|
|
3
|
+
* Its surface is the one `UiCodeBlock` renders for fenced code inside
|
|
4
|
+
* {@link CkMarkdownRenderer}: the same bordered, rounded panel, the same muted
|
|
5
|
+
* header carrying the language and a copy button, the same monospace metrics,
|
|
6
|
+
* and syntax colours approximating Shiki's `github-light` / `github-dark`. A
|
|
7
|
+
* snippet therefore looks the same whether it is being read in a reply or
|
|
8
|
+
* edited here.
|
|
9
|
+
*
|
|
10
|
+
* Use it wherever a whole document *is* code — a snippet field, a config or
|
|
11
|
+
* template editor, a "create skill" form. For prose that merely contains code,
|
|
12
|
+
* use {@link CkMilkdownEditor}, whose fenced blocks share this same theme.
|
|
13
|
+
*
|
|
14
|
+
* Grammars load on demand from CodeMirror's language data, so only the
|
|
15
|
+
* languages actually opened are fetched, and the editor itself loads lazily on
|
|
16
|
+
* mount so importing the component costs nothing until one is shown.
|
|
17
|
+
*
|
|
18
|
+
* @category Form Inputs
|
|
19
|
+
* @useCases edit a code snippet, config or template editor, code field in a form, review and tweak generated code
|
|
20
|
+
* @keywords code, editor, codemirror, syntax highlighting, snippet, v-model, monospace, language
|
|
21
|
+
* @related CkMilkdownEditor, CkMarkdownRenderer
|
|
22
|
+
*/
|
|
23
|
+
export interface CkCodeEditorProps {
|
|
24
|
+
/**
|
|
25
|
+
* The code. Supports `v-model`.
|
|
26
|
+
*
|
|
27
|
+
* Unlike {@link CkMilkdownEditor}, updates are emitted synchronously on every
|
|
28
|
+
* change, with no debounce and no reformatting of what you pass in.
|
|
29
|
+
*
|
|
30
|
+
* @default ''
|
|
31
|
+
*/
|
|
32
|
+
modelValue?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Language id used for both syntax highlighting and the header label, for
|
|
35
|
+
* example `typescript`, `python`, `json` or `bash`. Matching is on the names,
|
|
36
|
+
* aliases and extensions CodeMirror knows; an unknown value still renders as
|
|
37
|
+
* plain text rather than failing.
|
|
38
|
+
*
|
|
39
|
+
* @default 'text'
|
|
40
|
+
*/
|
|
41
|
+
language?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Filename shown in the header instead of the language, mirroring the prop of
|
|
44
|
+
* the same name on `UiCodeBlock`.
|
|
45
|
+
*/
|
|
46
|
+
filename?: string;
|
|
47
|
+
/** Text shown while the document is empty. */
|
|
48
|
+
placeholder?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Render the code without allowing edits. The caret and the active-line
|
|
51
|
+
* highlight are hidden, but the text stays selectable and copyable.
|
|
52
|
+
*
|
|
53
|
+
* @default false
|
|
54
|
+
*/
|
|
55
|
+
readonly?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Like `readonly`, but also mutes the editor and marks it `aria-disabled`.
|
|
58
|
+
*
|
|
59
|
+
* @default false
|
|
60
|
+
*/
|
|
61
|
+
disabled?: boolean;
|
|
62
|
+
/** Accessible name for the editable region. */
|
|
63
|
+
ariaLabel?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Show the header bar with the language or filename and the copy button. Set
|
|
66
|
+
* it to `false` for a bare editing surface.
|
|
67
|
+
*
|
|
68
|
+
* @default true
|
|
69
|
+
*/
|
|
70
|
+
showHeader?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Show the copy button in the header.
|
|
73
|
+
*
|
|
74
|
+
* @default true
|
|
75
|
+
*/
|
|
76
|
+
showCopyButton?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Show the line-number gutter. On by default, which is what a dedicated code
|
|
79
|
+
* editor is expected to do; `UiCodeBlock` and `CkMilkdownEditor` default to
|
|
80
|
+
* `false` because their code is being read rather than written.
|
|
81
|
+
*
|
|
82
|
+
* @default true
|
|
83
|
+
*/
|
|
84
|
+
showLineNumbers?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Wrap long lines instead of scrolling horizontally. `UiCodeBlock` scrolls,
|
|
87
|
+
* so the default keeps the two consistent.
|
|
88
|
+
*
|
|
89
|
+
* @default false
|
|
90
|
+
*/
|
|
91
|
+
lineWrapping?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Maximum height of the scrollable code area, as any CSS length, for example
|
|
94
|
+
* `'24rem'`. Unset, the editor grows with its content.
|
|
95
|
+
*/
|
|
96
|
+
maxHeight?: string;
|
|
97
|
+
/**
|
|
98
|
+
* Move focus into the editor once it has been created.
|
|
99
|
+
*
|
|
100
|
+
* @default false
|
|
101
|
+
*/
|
|
102
|
+
autofocus?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Clipboard writer for the copy button. Injected for tests and for hosts that
|
|
105
|
+
* proxy clipboard access; defaults to `navigator.clipboard.writeText`.
|
|
106
|
+
*/
|
|
107
|
+
writeToClipboard?: (text: string) => Promise<void>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Events emitted by {@link CkCodeEditor}. */
|
|
111
|
+
export interface CkCodeEditorEmits {
|
|
112
|
+
/** Emitted on every document change, with the full code. */
|
|
113
|
+
'update:modelValue': [value: string];
|
|
114
|
+
/** Emitted when the editing area gains focus. */
|
|
115
|
+
focus: [];
|
|
116
|
+
/** Emitted when focus leaves the editor entirely, header included. */
|
|
117
|
+
blur: [];
|
|
118
|
+
/** Emitted after the copy button runs, whether or not the write succeeded. */
|
|
119
|
+
copy: [payload: { success: boolean }];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Methods exposed by {@link CkCodeEditor} through a template ref. */
|
|
123
|
+
export interface CkCodeEditorExposed {
|
|
124
|
+
/** Focus the editing area. No-op until the editor has been created. */
|
|
125
|
+
focus: () => void;
|
|
126
|
+
/** Read the current document synchronously. */
|
|
127
|
+
getCode: () => string;
|
|
128
|
+
}
|
|
@@ -142,6 +142,19 @@ describe('CkMilkdownEditor', () => {
|
|
|
142
142
|
).toBe('false');
|
|
143
143
|
});
|
|
144
144
|
|
|
145
|
+
it('toggles the line-number gutter with `showLineNumbers`', async () => {
|
|
146
|
+
const { container, rerender } = await renderEditor({
|
|
147
|
+
modelValue: 'Hello',
|
|
148
|
+
showLineNumbers: true,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const root = () => container.querySelector('.ck-milkdown');
|
|
152
|
+
expect(root()?.className).toContain('ck-milkdown--line-numbers');
|
|
153
|
+
|
|
154
|
+
await rerender({ modelValue: 'Hello', showLineNumbers: false });
|
|
155
|
+
expect(root()?.className).not.toContain('ck-milkdown--line-numbers');
|
|
156
|
+
});
|
|
157
|
+
|
|
145
158
|
it('exposes the editable region as a labelled textbox', async () => {
|
|
146
159
|
const { container } = await renderEditor({
|
|
147
160
|
modelValue: 'Hello',
|
|
@@ -25,6 +25,7 @@ const meta: Meta<typeof CkMilkdownEditor> = {
|
|
|
25
25
|
readonly: { control: 'boolean' },
|
|
26
26
|
disabled: { control: 'boolean' },
|
|
27
27
|
autofocus: { control: 'boolean' },
|
|
28
|
+
showLineNumbers: { control: 'boolean' },
|
|
28
29
|
},
|
|
29
30
|
};
|
|
30
31
|
|
|
@@ -109,6 +110,21 @@ const markdown = '# Read me\\n\\nThis document **cannot** be edited.';
|
|
|
109
110
|
<CkMilkdownEditor :model-value="markdown" readonly aria-label="Read-only document" />
|
|
110
111
|
</template>`;
|
|
111
112
|
|
|
113
|
+
const withLineNumbersTemplateSource = `<script setup lang="ts">
|
|
114
|
+
import { ref } from 'vue';
|
|
115
|
+
import { CkMilkdownEditor } from '@aleph-alpha/chat-kit';
|
|
116
|
+
|
|
117
|
+
const markdown = ref('# Notes\\n\\nA fenced block with a gutter.');
|
|
118
|
+
</script>
|
|
119
|
+
|
|
120
|
+
<template>
|
|
121
|
+
<CkMilkdownEditor
|
|
122
|
+
v-model="markdown"
|
|
123
|
+
show-line-numbers
|
|
124
|
+
aria-label="Editor with line numbers"
|
|
125
|
+
/>
|
|
126
|
+
</template>`;
|
|
127
|
+
|
|
112
128
|
const disabledTemplateSource = `<script setup lang="ts">
|
|
113
129
|
import { CkMilkdownEditor } from '@aleph-alpha/chat-kit';
|
|
114
130
|
|
|
@@ -210,6 +226,24 @@ export const Readonly: Story = {
|
|
|
210
226
|
parameters: { docs: { source: { code: readonlyTemplateSource } } },
|
|
211
227
|
};
|
|
212
228
|
|
|
229
|
+
/**
|
|
230
|
+
* `showLineNumbers` reveals the line-number gutter on fenced code blocks. It
|
|
231
|
+
* matches the prop of the same name on `UiCodeBlock`, so the same flag can keep
|
|
232
|
+
* the editor and the rendered message consistent.
|
|
233
|
+
*/
|
|
234
|
+
export const WithLineNumbers: Story = {
|
|
235
|
+
render: (args) => ({
|
|
236
|
+
components: { CkMilkdownEditor },
|
|
237
|
+
setup() {
|
|
238
|
+
const markdown = ref(args.modelValue ?? '');
|
|
239
|
+
return { args, markdown };
|
|
240
|
+
},
|
|
241
|
+
template: `<CkMilkdownEditor v-bind="args" v-model="markdown" aria-label="Editor with line numbers" />`,
|
|
242
|
+
}),
|
|
243
|
+
args: { modelValue: richContent, showLineNumbers: true },
|
|
244
|
+
parameters: { docs: { source: { code: withLineNumbersTemplateSource } } },
|
|
245
|
+
};
|
|
246
|
+
|
|
213
247
|
/**
|
|
214
248
|
* `disabled` also mutes the content and blocks pointer interaction, and sets
|
|
215
249
|
* `aria-disabled` on the root.
|
|
@@ -22,6 +22,7 @@ const props = withDefaults(defineProps<CkMilkdownEditorProps>(), {
|
|
|
22
22
|
ariaLabel: undefined,
|
|
23
23
|
classes: () => ({}),
|
|
24
24
|
autofocus: false,
|
|
25
|
+
showLineNumbers: false,
|
|
25
26
|
});
|
|
26
27
|
|
|
27
28
|
const emit = defineEmits<CkMilkdownEditorEmits>();
|
|
@@ -132,9 +133,10 @@ defineExpose({ focus, getMarkdown });
|
|
|
132
133
|
<div
|
|
133
134
|
ref="rootRef"
|
|
134
135
|
:class="[
|
|
135
|
-
'ck-milkdown min-w-0 break-words',
|
|
136
|
+
'ck-milkdown ck-code-surface min-w-0 break-words',
|
|
136
137
|
isReady && 'ck-milkdown--ready',
|
|
137
138
|
disabled && 'ck-milkdown--disabled',
|
|
139
|
+
showLineNumbers && 'ck-milkdown--line-numbers',
|
|
138
140
|
]"
|
|
139
141
|
:aria-disabled="disabled || undefined"
|
|
140
142
|
@focusin="onFocusIn"
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type ChatKitLabels,
|
|
7
7
|
} from '../../../handlers/useChatKitLabels';
|
|
8
8
|
import type { CkMarkdownElementClasses } from '../CkMarkdownRenderer';
|
|
9
|
-
import { ckCodeMirrorTheme } from '
|
|
9
|
+
import { ckCodeMirrorTheme } from '../../../helpers/codemirror/theme';
|
|
10
10
|
import { crepeIcons } from './crepeIcons';
|
|
11
11
|
import { applyMarkdownClasses } from './markdownClassAttrs';
|
|
12
12
|
|
|
@@ -24,6 +24,7 @@ import '@milkdown/crepe/theme/common/list-item.css';
|
|
|
24
24
|
import '@milkdown/crepe/theme/common/link-tooltip.css';
|
|
25
25
|
import '@milkdown/crepe/theme/common/toolbar.css';
|
|
26
26
|
import '@milkdown/crepe/theme/common/code-mirror.css';
|
|
27
|
+
import '../../../helpers/codemirror/code-palette.css';
|
|
27
28
|
import './milkdown-theme.css';
|
|
28
29
|
|
|
29
30
|
export interface CreateCrepeEditorOptions {
|
|
@@ -49,36 +49,12 @@
|
|
|
49
49
|
--crepe-shadow-1: var(--shadow-md);
|
|
50
50
|
--crepe-shadow-2: var(--shadow-lg);
|
|
51
51
|
|
|
52
|
-
/*
|
|
53
|
-
|
|
54
|
-
--ck-cm-foreground: #24292f;
|
|
55
|
-
--ck-cm-keyword: #cf222e;
|
|
56
|
-
--ck-cm-string: #0a3069;
|
|
57
|
-
--ck-cm-comment: #6e7781;
|
|
58
|
-
--ck-cm-function: #8250df;
|
|
59
|
-
--ck-cm-constant: #0550ae;
|
|
60
|
-
/* UiCodeBlock rewrites Shiki's orange entity colour to the primary text token. */
|
|
61
|
-
--ck-cm-entity: oklch(var(--content-on-surface-primary));
|
|
62
|
-
--ck-cm-tag: #116329;
|
|
63
|
-
--ck-cm-error: #82071e;
|
|
64
|
-
--ck-cm-selection: oklch(var(--background-accent-soft));
|
|
52
|
+
/* The `--ck-cm-*` syntax palette comes from the shared `code-palette.css`
|
|
53
|
+
* via the `ck-code-surface` class on this same root. */
|
|
65
54
|
|
|
66
55
|
position: relative;
|
|
67
56
|
}
|
|
68
57
|
|
|
69
|
-
/* CodeMirror palette ≈ Shiki `github-dark`. */
|
|
70
|
-
html.dark .ck-milkdown {
|
|
71
|
-
--ck-cm-bg: #24292e;
|
|
72
|
-
--ck-cm-foreground: #c9d1d9;
|
|
73
|
-
--ck-cm-keyword: #ff7b72;
|
|
74
|
-
--ck-cm-string: #a5d6ff;
|
|
75
|
-
--ck-cm-comment: #8b949e;
|
|
76
|
-
--ck-cm-function: #d2a8ff;
|
|
77
|
-
--ck-cm-constant: #79c0ff;
|
|
78
|
-
--ck-cm-tag: #7ee787;
|
|
79
|
-
--ck-cm-error: #ffa198;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
58
|
/* ------------------------------------------------------------------------ */
|
|
83
59
|
/* Minimal reset (replaces Crepe's reset.css) */
|
|
84
60
|
/* ------------------------------------------------------------------------ */
|
|
@@ -407,10 +383,47 @@ html.dark .ck-milkdown {
|
|
|
407
383
|
color: var(--ck-cm-foreground);
|
|
408
384
|
}
|
|
409
385
|
|
|
386
|
+
/* Line-number gutter. CodeMirror's `basicSetup` (which Crepe always loads)
|
|
387
|
+
* always renders it, so `showLineNumbers` only toggles this class — no
|
|
388
|
+
* reconfiguration, and the change applies to code blocks already on screen.
|
|
389
|
+
* Hidden by default to match `UiCodeBlock`, whose `showLineNumbers` is also
|
|
390
|
+
* false by default, so a document reads the same in the editor and in
|
|
391
|
+
* `CkMarkdownRenderer`. */
|
|
410
392
|
.ck-milkdown .milkdown .milkdown-code-block .cm-gutters {
|
|
411
393
|
display: none;
|
|
412
394
|
}
|
|
413
395
|
|
|
396
|
+
.ck-milkdown--line-numbers .milkdown .milkdown-code-block .cm-gutters {
|
|
397
|
+
display: flex;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/* `basicSetup` also installs a code-folding gutter. Its chevrons are noise in
|
|
401
|
+
* a prose editor and have no counterpart in `UiCodeBlock`, so only the numbers
|
|
402
|
+
* are ever shown. `!important` is required because CodeMirror's own base theme
|
|
403
|
+
* declares `.cm-gutter { display: flex !important }`, which no amount of
|
|
404
|
+
* specificity can outrank. Crepe always passes `basicSetup`, so the extension
|
|
405
|
+
* cannot simply be left out. */
|
|
406
|
+
.ck-milkdown .milkdown .milkdown-code-block .cm-foldGutter {
|
|
407
|
+
display: none !important;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.ck-milkdown--line-numbers
|
|
411
|
+
.milkdown
|
|
412
|
+
.milkdown-code-block
|
|
413
|
+
.cm-lineNumbers
|
|
414
|
+
.cm-gutterElement {
|
|
415
|
+
min-width: 2.25rem;
|
|
416
|
+
padding: 0 0.75rem 0 1rem;
|
|
417
|
+
color: oklch(var(--content-on-surface-muted));
|
|
418
|
+
text-align: right;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/* The gutter supplies the left inset once it is visible, so the code itself
|
|
422
|
+
* should not pay for it twice. */
|
|
423
|
+
.ck-milkdown--line-numbers .milkdown .milkdown-code-block .cm-content {
|
|
424
|
+
padding-left: 0;
|
|
425
|
+
}
|
|
426
|
+
|
|
414
427
|
/* Shown while the CodeMirror instance is still mounting. */
|
|
415
428
|
.ck-milkdown .milkdown .milkdown-code-block .milkdown-code-block-placeholder {
|
|
416
429
|
margin: 0;
|
|
@@ -71,6 +71,14 @@ export interface CkMilkdownEditorProps {
|
|
|
71
71
|
* @default false
|
|
72
72
|
*/
|
|
73
73
|
autofocus?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Show a line-number gutter on fenced code blocks. Matches the prop of the
|
|
76
|
+
* same name on `UiCodeBlock`, so one flag can drive the editor and the
|
|
77
|
+
* rendered message alike. Applies to code blocks already on screen.
|
|
78
|
+
*
|
|
79
|
+
* @default false
|
|
80
|
+
*/
|
|
81
|
+
showLineNumbers?: boolean;
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
/** Events emitted by {@link CkMilkdownEditor}. */
|
|
@@ -86,3 +86,9 @@ export type {
|
|
|
86
86
|
CkMilkdownEditorExposed,
|
|
87
87
|
CkMilkdownEditorProps,
|
|
88
88
|
} from './CkMilkdownEditor';
|
|
89
|
+
export { CkCodeEditor } from './CkCodeEditor';
|
|
90
|
+
export type {
|
|
91
|
+
CkCodeEditorEmits,
|
|
92
|
+
CkCodeEditorExposed,
|
|
93
|
+
CkCodeEditorProps,
|
|
94
|
+
} from './CkCodeEditor';
|
package/src/components/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ export {
|
|
|
28
28
|
CkUserMessage,
|
|
29
29
|
CkEditableUserMessage,
|
|
30
30
|
CkMilkdownEditor,
|
|
31
|
+
CkCodeEditor,
|
|
31
32
|
} from './base';
|
|
32
33
|
export type {
|
|
33
34
|
CkAgentMessageHeaderProps,
|
|
@@ -70,6 +71,9 @@ export type {
|
|
|
70
71
|
CkMilkdownEditorEmits,
|
|
71
72
|
CkMilkdownEditorExposed,
|
|
72
73
|
CkMilkdownEditorProps,
|
|
74
|
+
CkCodeEditorEmits,
|
|
75
|
+
CkCodeEditorExposed,
|
|
76
|
+
CkCodeEditorProps,
|
|
73
77
|
} from './base';
|
|
74
78
|
|
|
75
79
|
// Composed (pattern) components
|
|
@@ -111,6 +111,10 @@ export type ChatKitLabels = {
|
|
|
111
111
|
* set and more conversations exist than the limit.
|
|
112
112
|
*/
|
|
113
113
|
historyShowAll?: string;
|
|
114
|
+
/** Accessible name of the copy button in the `CkCodeEditor` header. */
|
|
115
|
+
codeEditorCopy?: string;
|
|
116
|
+
/** Accessible name of the `CkCodeEditor` copy button just after a copy. */
|
|
117
|
+
codeEditorCopied?: string;
|
|
114
118
|
/** Accessible name of the "Bold" button in the `CkMilkdownEditor` toolbar. */
|
|
115
119
|
markdownEditorBold?: string;
|
|
116
120
|
/** Accessible name of the "Italic" button in the `CkMilkdownEditor` toolbar. */
|
|
@@ -168,6 +172,8 @@ export const defaultChatKitLabels = {
|
|
|
168
172
|
historyItemEditAction: 'Edit',
|
|
169
173
|
historyItemDeleteAction: 'Delete',
|
|
170
174
|
historyShowAll: 'Show all',
|
|
175
|
+
codeEditorCopy: 'Copy code',
|
|
176
|
+
codeEditorCopied: 'Copied',
|
|
171
177
|
markdownEditorBold: 'Bold',
|
|
172
178
|
markdownEditorItalic: 'Italic',
|
|
173
179
|
markdownEditorStrikethrough: 'Strikethrough',
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Syntax palette shared by every CodeMirror surface in chat-kit — the code
|
|
3
|
+
* blocks inside `CkMilkdownEditor` and the standalone `CkCodeEditor`.
|
|
4
|
+
*
|
|
5
|
+
* The values are Shiki's `github-light` and `github-dark`, which is what
|
|
6
|
+
* `UiCodeBlock` renders inside `CkMarkdownRenderer`, read off a rendered block
|
|
7
|
+
* rather than copied from memory. A snippet therefore keeps its colours whether
|
|
8
|
+
* it is being read or edited. `theme.ts` maps CodeMirror's syntax tags onto
|
|
9
|
+
* these variables; nothing else should hardcode a code colour.
|
|
10
|
+
*
|
|
11
|
+
* Apply `.ck-code-surface` to the root of any component hosting a CodeMirror
|
|
12
|
+
* instance. The dark values follow `html.dark`, matching how `UiCodeBlock`
|
|
13
|
+
* switches its own Shiki theme.
|
|
14
|
+
*
|
|
15
|
+
* Exact parity is not achievable for every token: Shiki classifies with
|
|
16
|
+
* TextMate grammars and CodeMirror with Lezer, and the two disagree at the
|
|
17
|
+
* edges. The mapping in `theme.ts` targets the classes that actually occur in
|
|
18
|
+
* everyday code.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
.ck-code-surface {
|
|
22
|
+
--ck-cm-bg: #ffffff;
|
|
23
|
+
--ck-cm-foreground: #24292e;
|
|
24
|
+
--ck-cm-keyword: #d73a49;
|
|
25
|
+
--ck-cm-string: #032f62;
|
|
26
|
+
--ck-cm-comment: #6a737d;
|
|
27
|
+
/* Declared functions, classes and interfaces. */
|
|
28
|
+
--ck-cm-function: #6f42c1;
|
|
29
|
+
/* Built-in types, literals and plain variables. */
|
|
30
|
+
--ck-cm-constant: #005cc5;
|
|
31
|
+
/* `UiCodeBlock` rewrites Shiki's orange (#e36209 / #ffab70) to the primary
|
|
32
|
+
* text token, which is what declared property names end up as. */
|
|
33
|
+
--ck-cm-property: oklch(var(--content-on-surface-primary));
|
|
34
|
+
--ck-cm-tag: #22863a;
|
|
35
|
+
--ck-cm-error: #b31d28;
|
|
36
|
+
--ck-cm-selection: oklch(var(--background-accent-soft));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
html.dark .ck-code-surface {
|
|
40
|
+
--ck-cm-bg: #24292e;
|
|
41
|
+
--ck-cm-foreground: #e1e4e8;
|
|
42
|
+
--ck-cm-keyword: #f97583;
|
|
43
|
+
--ck-cm-string: #9ecbff;
|
|
44
|
+
--ck-cm-comment: #6a737d;
|
|
45
|
+
--ck-cm-function: #b392f0;
|
|
46
|
+
--ck-cm-constant: #79b8ff;
|
|
47
|
+
--ck-cm-tag: #85e89d;
|
|
48
|
+
--ck-cm-error: #fdaeb7;
|
|
49
|
+
}
|