@aleph-alpha/chat-kit 5.13.0 → 6.1.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-BNcJURkq.js → CkHistory.vue_vue_type_script_setup_true_lang-Cr8vF94e.js} +221 -115
- package/dist/adapters/responses-api.d.ts +1 -1
- package/dist/adapters/responses-api.d.ts.map +1 -1
- package/dist/adapters/responses-api.js +9 -7
- package/dist/apl-fqmucPXA.js +140 -0
- package/dist/asciiarmor-DucZyvP0.js +56 -0
- package/dist/asn1-BnOEsgAm.js +144 -0
- package/dist/asterisk-QAlztEwS.js +345 -0
- package/dist/brainfuck-DZVCuF_t.js +53 -0
- package/dist/chat-kit.css +1177 -12
- package/dist/clike-CTYGlS6x.js +799 -0
- package/dist/clojure-BhXMqnxz.js +849 -0
- package/dist/cmake-BGaNd9E7.js +71 -0
- package/dist/cobol-4yqQntpt.js +120 -0
- package/dist/coffeescript-D2dXvhEc.js +308 -0
- package/dist/commonlisp-CF_VNHQR.js +130 -0
- package/dist/components/base/CkHistoryItem/CkHistoryItem.vue.d.ts.map +1 -1
- package/dist/components/base/CkHistoryWrapper/CkHistoryWrapper.vue.d.ts.map +1 -1
- package/dist/components/base/CkMarkdownRenderer/CkMarkdownRenderer.stories.d.ts.map +1 -1
- package/dist/components/base/CkMilkdownEditor/CkMilkdownEditor.stories.d.ts +41 -0
- package/dist/components/base/CkMilkdownEditor/CkMilkdownEditor.stories.d.ts.map +1 -0
- package/dist/components/base/CkMilkdownEditor/CkMilkdownEditor.vue.d.ts +26 -0
- package/dist/components/base/CkMilkdownEditor/CkMilkdownEditor.vue.d.ts.map +1 -0
- package/dist/components/base/CkMilkdownEditor/codeMirrorTheme.d.ts +20 -0
- package/dist/components/base/CkMilkdownEditor/codeMirrorTheme.d.ts.map +1 -0
- package/dist/components/base/CkMilkdownEditor/createCrepeEditor.d.ts +42 -0
- package/dist/components/base/CkMilkdownEditor/createCrepeEditor.d.ts.map +1 -0
- package/dist/components/base/CkMilkdownEditor/crepeIcons.d.ts +33 -0
- package/dist/components/base/CkMilkdownEditor/crepeIcons.d.ts.map +1 -0
- package/dist/components/base/CkMilkdownEditor/index.d.ts +3 -0
- package/dist/components/base/CkMilkdownEditor/index.d.ts.map +1 -0
- package/dist/components/base/CkMilkdownEditor/markdownClassAttrs.d.ts +16 -0
- package/dist/components/base/CkMilkdownEditor/markdownClassAttrs.d.ts.map +1 -0
- package/dist/components/base/CkMilkdownEditor/types.d.ts +98 -0
- package/dist/components/base/CkMilkdownEditor/types.d.ts.map +1 -0
- 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 +12 -11
- package/dist/composables/fileUpload/useFileUpload.d.ts.map +1 -1
- package/dist/composables/index.js +1 -1
- package/dist/composables/useChat.d.ts.map +1 -1
- package/dist/composables/useConversations.d.ts.map +1 -1
- package/dist/createCrepeEditor-CFbvSH3s.js +74141 -0
- package/dist/crystal-DyuLTqLs.js +398 -0
- package/dist/css-DkSyTW67.js +1772 -0
- package/dist/cypher-Dlu_3r4V.js +121 -0
- package/dist/d-UURgV0Ux.js +179 -0
- package/dist/diff-B_Bi2Crb.js +25 -0
- package/dist/dockerfile-Bvk733Ga.js +201 -0
- package/dist/dtd-Dy74G54E.js +114 -0
- package/dist/dylan-TSb-Nfix.js +314 -0
- package/dist/ebnf-4fKAGW3a.js +140 -0
- package/dist/ecl-B59qGGVg.js +178 -0
- package/dist/eiffel-Dze7nlu3.js +134 -0
- package/dist/elm-DG7jkhNZ.js +176 -0
- package/dist/erlang-BO6gOnGA.js +674 -0
- package/dist/factor-CMxFHDqz.js +65 -0
- package/dist/fcl-CDDUNjTj.js +141 -0
- package/dist/forth-B9D2JCeE.js +116 -0
- package/dist/fortran-CAG2BFbe.js +467 -0
- package/dist/gas-0Aw8zDr5.js +293 -0
- package/dist/gherkin-DhZlEZiy.js +115 -0
- package/dist/groovy-CpwJiBl7.js +223 -0
- package/dist/handlers/index.js +1 -1
- package/dist/handlers/useChatKitLabels.d.ts +62 -1
- package/dist/handlers/useChatKitLabels.d.ts.map +1 -1
- package/dist/haskell-ySd-OUo8.js +459 -0
- package/dist/haxe-7MlzfeYV.js +514 -0
- package/dist/http-BqypyemW.js +79 -0
- package/dist/idl-4HIGJlDI.js +985 -0
- package/dist/index-A5rWjRtZ.js +689 -0
- package/dist/index-B0ldhzaj.js +385 -0
- package/dist/index-BVOjyn3y.js +2490 -0
- package/dist/index-BWvkps_G.js +309 -0
- package/dist/index-BaS8OW8V.js +151 -0
- package/dist/index-BlHHEbV-.js +406 -0
- package/dist/index-BoqzHVNB.js +82 -0
- package/dist/index-C6zYjwIQ.js +704 -0
- package/dist/{index-Dn4ctxk8.js → index-C72e57-5.js} +48 -24
- package/dist/index-CMJGuXap.js +330 -0
- package/dist/index-CRfGWVdQ.js +116 -0
- package/dist/index-C_gTb50u.js +81 -0
- package/dist/index-DSU7cBGz.js +178 -0
- package/dist/index-DSq3gvqU.js +1767 -0
- package/dist/index-Da3777l2.js +289 -0
- package/dist/index-DdlzabmR.js +1021 -0
- package/dist/index-DhY_9bdS.js +61 -0
- package/dist/index-Dm6C1blN.js +1052 -0
- package/dist/index-Tj3pzx-E.js +327 -0
- package/dist/index-ZeRS5rER.js +97 -0
- package/dist/index-gzleljO6.js +157 -0
- package/dist/index-naWlQpaL.js +642 -0
- package/dist/index-sKO1lHHC.js +312 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -14
- package/dist/javascript-Bt8B7yTi.js +992 -0
- package/dist/julia-Bs6JJhYG.js +407 -0
- package/dist/livescript-DmzgM3Yt.js +296 -0
- package/dist/lua-Cvg8VrAA.js +256 -0
- package/dist/mathematica-DNLOL9PQ.js +110 -0
- package/dist/mbox-Ga7d4MMN.js +117 -0
- package/dist/mirc-Dma3B8rS.js +107 -0
- package/dist/mllike-DHn7xckP.js +334 -0
- package/dist/modelica-0d55jYY0.js +147 -0
- package/dist/mscgen-DdqZYINH.js +135 -0
- package/dist/mumps-Btr8VblO.js +93 -0
- package/dist/nginx-DTDtBDVN.js +141 -0
- package/dist/nsis-CyKBdPWp.js +62 -0
- package/dist/ntriples-CvgOYMpL.js +153 -0
- package/dist/octave-DYBj3-tl.js +200 -0
- package/dist/oz-R_e8WMIi.js +231 -0
- package/dist/pascal-GD8iposT.js +105 -0
- package/dist/perl-DL9mHpoi.js +1105 -0
- package/dist/pig-C_4T4YIV.js +101 -0
- package/dist/powershell-B0suO7Vd.js +328 -0
- package/dist/properties-BR-vP1aU.js +58 -0
- package/dist/protobuf-BxgpyhoW.js +77 -0
- package/dist/pug-vHnWR0UE.js +405 -0
- package/dist/puppet-Bdao66PW.js +137 -0
- package/dist/python-BFGRmuZ9.js +426 -0
- package/dist/q-CrbCVq4a.js +131 -0
- package/dist/r-V7nswm59.js +170 -0
- package/dist/rpm-C-DLY-If.js +109 -0
- package/dist/ruby-CQTpZw6f.js +331 -0
- package/dist/sas-D2UG-yhZ.js +207 -0
- package/dist/scheme-BKzrkGJD.js +222 -0
- package/dist/shell-BlsXDxCn.js +222 -0
- package/dist/sieve-CjwBwOY5.js +135 -0
- package/dist/simple-mode-DMneyfDu.js +130 -0
- package/dist/smalltalk-BOIGQuhN.js +121 -0
- package/dist/solr-CwD7U71z.js +69 -0
- package/dist/sparql-DYskk2vE.js +249 -0
- package/dist/spreadsheet-Bgtt3oLP.js +87 -0
- package/dist/sql-Cei9CMfk.js +342 -0
- package/dist/stex-C1nZSzAw.js +230 -0
- package/dist/stylus-BkS-boTH.js +565 -0
- package/dist/swift-FRZi1uvB.js +291 -0
- package/dist/tcl-CUcaCdmq.js +114 -0
- package/dist/test-utils/createTextPlugin.d.ts +7 -1
- package/dist/test-utils/createTextPlugin.d.ts.map +1 -1
- package/dist/textile-BnFpjsrl.js +414 -0
- package/dist/tiddlywiki-CjprD-Qp.js +218 -0
- package/dist/tiki-DK9DOeWn.js +268 -0
- package/dist/toml-BOuWGMcf.js +76 -0
- package/dist/troff-E1bJ0PPL.js +61 -0
- package/dist/ttcn-cfg-Dc39-fIP.js +133 -0
- package/dist/ttcn-tKd4HLu4.js +192 -0
- package/dist/turtle-Dq7-1WAf.js +124 -0
- package/dist/{useChatKitLabels-CMOr3Kjq.js → useChatKitLabels-Ww1Q7xUr.js} +26 -3
- package/dist/vb-Dp90gtsv.js +196 -0
- package/dist/vbscript-Bfn8O8I7.js +478 -0
- package/dist/velocity-BwIZK1TH.js +149 -0
- package/dist/verilog-CnT9bMk0.js +429 -0
- package/dist/vhdl-DCkMIyT9.js +158 -0
- package/dist/webidl-BTLTThCm.js +204 -0
- package/dist/xquery-BrBUuxMR.js +525 -0
- package/dist/yacas-b5lAVEIl.js +130 -0
- package/dist/z80-ClgwfNdB.js +92 -0
- package/package.json +11 -5
- package/src/adapters/responses-api.spec.ts +86 -0
- package/src/adapters/responses-api.ts +13 -9
- package/src/components/base/CkCitationCard/CkCitationCard.vue +1 -1
- package/src/components/base/CkCitationLabel/CkCitationLabel.vue +1 -1
- package/src/components/base/CkConversationLayout/CkConversationLayout.spec.ts +1 -5
- package/src/components/base/CkCopyMessageButton/CkCopyMessageButton.spec.ts +1 -1
- package/src/components/base/CkFeedbackButton/CkFeedbackButton.vue +3 -3
- package/src/components/base/CkFeedbackDialog/CkFeedbackDialog.vue +9 -9
- package/src/components/base/CkHistoryItem/CkHistoryItem.vue +5 -7
- package/src/components/base/CkHistoryWrapper/CkHistoryWrapper.vue +2 -1
- package/src/components/base/CkMarkdownRenderer/CkMarkdownRenderer.stories.ts +9 -4
- package/src/components/base/CkMarkdownRenderer/CkMarkdownRenderer.vue +1 -1
- package/src/components/base/CkMilkdownEditor/CkMilkdownEditor.spec.ts +221 -0
- package/src/components/base/CkMilkdownEditor/CkMilkdownEditor.stories.ts +274 -0
- package/src/components/base/CkMilkdownEditor/CkMilkdownEditor.vue +151 -0
- package/src/components/base/CkMilkdownEditor/codeMirrorTheme.ts +132 -0
- package/src/components/base/CkMilkdownEditor/createCrepeEditor.ts +192 -0
- package/src/components/base/CkMilkdownEditor/crepeIcons.ts +77 -0
- package/src/components/base/CkMilkdownEditor/index.ts +2 -0
- package/src/components/base/CkMilkdownEditor/markdownClassAttrs.ts +64 -0
- package/src/components/base/CkMilkdownEditor/milkdown-theme.css +441 -0
- package/src/components/base/CkMilkdownEditor/types.ts +100 -0
- package/src/components/base/CkModelSelector/CkModelSelector.vue +3 -3
- package/src/components/base/CkPromptInputDisclaimer/CkPromptInputDisclaimer.stories.ts +2 -2
- package/src/components/base/index.ts +6 -0
- package/src/components/index.ts +4 -0
- package/src/composables/fileUpload/useFileUpload.ts +45 -30
- package/src/composables/fileUpload/useFileUploadWrapper.spec.ts +1 -1
- package/src/composables/fileUpload/useFileUploadWrapper.ts +3 -3
- package/src/composables/useChat.ts +3 -1
- package/src/composables/useConversations.ts +8 -5
- package/src/handlers/useChatKitLabels.spec.ts +0 -1
- package/src/handlers/useChatKitLabels.ts +29 -2
- package/src/handlers/useMarkdownParser.ts +1 -1
- package/src/index.ts +4 -0
- package/src/test-setup.ts +3 -3
- package/src/test-utils/createTextPlugin.ts +11 -3
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
|
2
|
+
import type { Extension } from '@codemirror/state';
|
|
3
|
+
import { EditorView } from '@codemirror/view';
|
|
4
|
+
import { tags as t } from '@lezer/highlight';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Same monospace stack as `UiCodeBlock` in ui-library, so code in the editor
|
|
8
|
+
* and in the rendered message uses identical glyph metrics.
|
|
9
|
+
*/
|
|
10
|
+
export const CODE_FONT_FAMILY =
|
|
11
|
+
"ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace";
|
|
12
|
+
|
|
13
|
+
// Every colour is a CSS custom property so the light and dark palettes live in
|
|
14
|
+
// `milkdown-theme.css` (`.ck-milkdown` / `html.dark .ck-milkdown`) and switch
|
|
15
|
+
// with the rest of the design tokens — no JavaScript theme swap needed.
|
|
16
|
+
const cssVar = (name: string): string => `var(--ck-cm-${name})`;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Token → colour mapping approximating Shiki's `github-light` / `github-dark`
|
|
20
|
+
* themes, which `UiCodeBlock` uses, so highlighted code looks the same in the
|
|
21
|
+
* editor and in `CkMarkdownRenderer`.
|
|
22
|
+
*/
|
|
23
|
+
export const ckHighlightStyle = HighlightStyle.define([
|
|
24
|
+
{
|
|
25
|
+
tag: [
|
|
26
|
+
t.keyword,
|
|
27
|
+
t.modifier,
|
|
28
|
+
t.controlKeyword,
|
|
29
|
+
t.definitionKeyword,
|
|
30
|
+
t.moduleKeyword,
|
|
31
|
+
t.operatorKeyword,
|
|
32
|
+
],
|
|
33
|
+
color: cssVar('keyword'),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
tag: [t.string, t.special(t.string), t.regexp, t.escape, t.character],
|
|
37
|
+
color: cssVar('string'),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
tag: [t.comment, t.lineComment, t.blockComment, t.docComment],
|
|
41
|
+
color: cssVar('comment'),
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
tag: [
|
|
45
|
+
t.function(t.variableName),
|
|
46
|
+
t.function(t.propertyName),
|
|
47
|
+
t.definition(t.function(t.variableName)),
|
|
48
|
+
],
|
|
49
|
+
color: cssVar('function'),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
tag: [
|
|
53
|
+
t.number,
|
|
54
|
+
t.bool,
|
|
55
|
+
t.null,
|
|
56
|
+
t.atom,
|
|
57
|
+
t.constant(t.name),
|
|
58
|
+
t.constant(t.variableName),
|
|
59
|
+
t.propertyName,
|
|
60
|
+
t.attributeName,
|
|
61
|
+
t.attributeValue,
|
|
62
|
+
],
|
|
63
|
+
color: cssVar('constant'),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
tag: [
|
|
67
|
+
t.typeName,
|
|
68
|
+
t.className,
|
|
69
|
+
t.namespace,
|
|
70
|
+
t.definition(t.variableName),
|
|
71
|
+
t.self,
|
|
72
|
+
],
|
|
73
|
+
color: cssVar('entity'),
|
|
74
|
+
},
|
|
75
|
+
{ tag: [t.tagName, t.angleBracket], color: cssVar('tag') },
|
|
76
|
+
{
|
|
77
|
+
tag: [t.variableName, t.operator, t.punctuation, t.bracket, t.separator],
|
|
78
|
+
color: cssVar('foreground'),
|
|
79
|
+
},
|
|
80
|
+
{ tag: t.heading, fontWeight: 'bold' },
|
|
81
|
+
{ tag: t.strong, fontWeight: 'bold' },
|
|
82
|
+
{ tag: t.emphasis, fontStyle: 'italic' },
|
|
83
|
+
{ tag: t.link, textDecoration: 'underline' },
|
|
84
|
+
{ tag: t.invalid, color: cssVar('error') },
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* CodeMirror extension bundling the editor chrome (`UiCodeBlock`-like metrics:
|
|
89
|
+
* 14px mono text, 1.625 line height, 1rem padding, no gutter) with
|
|
90
|
+
* {@link ckHighlightStyle}. Pass it as the Crepe `CodeMirror` feature `theme`.
|
|
91
|
+
*/
|
|
92
|
+
export const ckCodeMirrorTheme: Extension = [
|
|
93
|
+
EditorView.theme({
|
|
94
|
+
'&': {
|
|
95
|
+
backgroundColor: cssVar('bg'),
|
|
96
|
+
color: cssVar('foreground'),
|
|
97
|
+
},
|
|
98
|
+
'.cm-scroller': {
|
|
99
|
+
fontFamily: CODE_FONT_FAMILY,
|
|
100
|
+
fontSize: '0.875rem',
|
|
101
|
+
lineHeight: '1.625',
|
|
102
|
+
},
|
|
103
|
+
'.cm-content': {
|
|
104
|
+
padding: '1rem',
|
|
105
|
+
caretColor: cssVar('foreground'),
|
|
106
|
+
},
|
|
107
|
+
'.cm-line': {
|
|
108
|
+
padding: '0',
|
|
109
|
+
},
|
|
110
|
+
'.cm-gutters': {
|
|
111
|
+
display: 'none',
|
|
112
|
+
},
|
|
113
|
+
'.cm-cursor, .cm-dropCursor': {
|
|
114
|
+
borderLeftColor: cssVar('foreground'),
|
|
115
|
+
},
|
|
116
|
+
'&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection':
|
|
117
|
+
{
|
|
118
|
+
backgroundColor: cssVar('selection'),
|
|
119
|
+
},
|
|
120
|
+
'.cm-activeLine': {
|
|
121
|
+
backgroundColor: 'transparent',
|
|
122
|
+
},
|
|
123
|
+
'&.cm-focused': {
|
|
124
|
+
outline: 'none',
|
|
125
|
+
},
|
|
126
|
+
'.cm-matchingBracket, .cm-nonmatchingBracket': {
|
|
127
|
+
backgroundColor: 'transparent',
|
|
128
|
+
outline: `1px solid ${cssVar('comment')}`,
|
|
129
|
+
},
|
|
130
|
+
}),
|
|
131
|
+
syntaxHighlighting(ckHighlightStyle),
|
|
132
|
+
];
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { Crepe, CrepeFeature, type CrepeConfig } from '@milkdown/crepe';
|
|
2
|
+
import { editorViewCtx, editorViewOptionsCtx } from '@milkdown/kit/core';
|
|
3
|
+
import { replaceAll } from '@milkdown/kit/utils';
|
|
4
|
+
import {
|
|
5
|
+
defaultChatKitLabels,
|
|
6
|
+
type ChatKitLabels,
|
|
7
|
+
} from '../../../handlers/useChatKitLabels';
|
|
8
|
+
import type { CkMarkdownElementClasses } from '../CkMarkdownRenderer';
|
|
9
|
+
import { ckCodeMirrorTheme } from './codeMirrorTheme';
|
|
10
|
+
import { crepeIcons } from './crepeIcons';
|
|
11
|
+
import { applyMarkdownClasses } from './markdownClassAttrs';
|
|
12
|
+
|
|
13
|
+
// Crepe's per-feature stylesheets, imported individually. `reset.css` (part of
|
|
14
|
+
// the bundled `theme/common/style.css`) is deliberately skipped: it styles
|
|
15
|
+
// headings, paragraphs, code, links and quotes with two-class selectors that
|
|
16
|
+
// would beat the utility classes `applyMarkdownClasses` puts on the nodes, and
|
|
17
|
+
// it forces a 60px/120px document padding. `milkdown-theme.css` replaces it
|
|
18
|
+
// and maps Crepe's `--crepe-*` variables to design tokens. It is imported last
|
|
19
|
+
// so its rules win ties.
|
|
20
|
+
import '@milkdown/crepe/theme/common/prosemirror.css';
|
|
21
|
+
import '@milkdown/crepe/theme/common/cursor.css';
|
|
22
|
+
import '@milkdown/crepe/theme/common/placeholder.css';
|
|
23
|
+
import '@milkdown/crepe/theme/common/list-item.css';
|
|
24
|
+
import '@milkdown/crepe/theme/common/link-tooltip.css';
|
|
25
|
+
import '@milkdown/crepe/theme/common/toolbar.css';
|
|
26
|
+
import '@milkdown/crepe/theme/common/code-mirror.css';
|
|
27
|
+
import './milkdown-theme.css';
|
|
28
|
+
|
|
29
|
+
export interface CreateCrepeEditorOptions {
|
|
30
|
+
/** Element the editor is mounted into. */
|
|
31
|
+
root: HTMLElement;
|
|
32
|
+
/** Initial markdown. */
|
|
33
|
+
defaultValue: string;
|
|
34
|
+
placeholder?: string;
|
|
35
|
+
ariaLabel?: string;
|
|
36
|
+
/** Start in read-only mode (applied before the view is created). */
|
|
37
|
+
readonly?: boolean;
|
|
38
|
+
/** Fully merged per-tag class map (defaults + consumer overrides). */
|
|
39
|
+
classes: CkMarkdownElementClasses;
|
|
40
|
+
labels: ChatKitLabels;
|
|
41
|
+
/** Called (debounced by Milkdown) with the serialised document on change. */
|
|
42
|
+
onMarkdownUpdated: (markdown: string) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Imperative surface `CkMilkdownEditor` drives; keeps Milkdown types out of the SFC. */
|
|
46
|
+
export interface CrepeEditorHandle {
|
|
47
|
+
crepe: Crepe;
|
|
48
|
+
/** Replace the whole document with new markdown. */
|
|
49
|
+
setMarkdown: (markdown: string) => void;
|
|
50
|
+
getMarkdown: () => string;
|
|
51
|
+
setReadonly: (readonly: boolean) => void;
|
|
52
|
+
focus: () => void;
|
|
53
|
+
destroy: () => void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type LabelKey = keyof typeof defaultChatKitLabels;
|
|
57
|
+
|
|
58
|
+
function pickLabel(labels: ChatKitLabels, key: LabelKey): string {
|
|
59
|
+
return labels[key] ?? defaultChatKitLabels[key];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const FEATURES: Partial<Record<CrepeFeature, boolean>> = {
|
|
63
|
+
[Crepe.Feature.Cursor]: true,
|
|
64
|
+
[Crepe.Feature.ListItem]: true,
|
|
65
|
+
[Crepe.Feature.LinkTooltip]: true,
|
|
66
|
+
[Crepe.Feature.Placeholder]: true,
|
|
67
|
+
[Crepe.Feature.Toolbar]: true,
|
|
68
|
+
[Crepe.Feature.CodeMirror]: true,
|
|
69
|
+
[Crepe.Feature.BlockEdit]: false,
|
|
70
|
+
[Crepe.Feature.ImageBlock]: false,
|
|
71
|
+
[Crepe.Feature.Table]: false,
|
|
72
|
+
[Crepe.Feature.Latex]: false,
|
|
73
|
+
[Crepe.Feature.TopBar]: false,
|
|
74
|
+
[Crepe.Feature.AI]: false,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
function buildFeatureConfigs(
|
|
78
|
+
options: CreateCrepeEditorOptions,
|
|
79
|
+
): NonNullable<CrepeConfig['featureConfigs']> {
|
|
80
|
+
const { labels } = options;
|
|
81
|
+
return {
|
|
82
|
+
[Crepe.Feature.Toolbar]: {
|
|
83
|
+
boldIcon: crepeIcons.bold,
|
|
84
|
+
italicIcon: crepeIcons.italic,
|
|
85
|
+
strikethroughIcon: crepeIcons.strikethrough,
|
|
86
|
+
codeIcon: crepeIcons.code,
|
|
87
|
+
linkIcon: crepeIcons.link,
|
|
88
|
+
boldLabel: pickLabel(labels, 'markdownEditorBold'),
|
|
89
|
+
italicLabel: pickLabel(labels, 'markdownEditorItalic'),
|
|
90
|
+
strikethroughLabel: pickLabel(labels, 'markdownEditorStrikethrough'),
|
|
91
|
+
codeLabel: pickLabel(labels, 'markdownEditorInlineCode'),
|
|
92
|
+
linkLabel: pickLabel(labels, 'markdownEditorLink'),
|
|
93
|
+
},
|
|
94
|
+
[Crepe.Feature.LinkTooltip]: {
|
|
95
|
+
linkIcon: crepeIcons.copy,
|
|
96
|
+
editButton: crepeIcons.pencil,
|
|
97
|
+
removeButton: crepeIcons.trash,
|
|
98
|
+
confirmButton: crepeIcons.check,
|
|
99
|
+
inputPlaceholder: pickLabel(labels, 'markdownEditorLinkPlaceholder'),
|
|
100
|
+
},
|
|
101
|
+
[Crepe.Feature.ListItem]: {
|
|
102
|
+
bulletIcon: crepeIcons.bullet,
|
|
103
|
+
checkBoxCheckedIcon: crepeIcons.squareCheck,
|
|
104
|
+
checkBoxUncheckedIcon: crepeIcons.square,
|
|
105
|
+
},
|
|
106
|
+
[Crepe.Feature.Placeholder]: {
|
|
107
|
+
text: options.placeholder ?? '',
|
|
108
|
+
mode: 'doc',
|
|
109
|
+
},
|
|
110
|
+
[Crepe.Feature.CodeMirror]: {
|
|
111
|
+
// Crepe's defaults provide the CodeMirror language list; only the theme
|
|
112
|
+
// and the chrome are replaced.
|
|
113
|
+
theme: ckCodeMirrorTheme,
|
|
114
|
+
copyIcon: crepeIcons.copy,
|
|
115
|
+
copyText: pickLabel(labels, 'markdownEditorCopyCode'),
|
|
116
|
+
expandIcon: crepeIcons.chevronDown,
|
|
117
|
+
searchIcon: crepeIcons.search,
|
|
118
|
+
clearSearchIcon: crepeIcons.close,
|
|
119
|
+
searchPlaceholder: pickLabel(labels, 'markdownEditorSearchLanguage'),
|
|
120
|
+
noResultText: pickLabel(labels, 'markdownEditorNoLanguageFound'),
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Build, configure and create a Crepe editor styled like `CkMarkdownRenderer`.
|
|
127
|
+
* Loaded lazily by `CkMilkdownEditor` so Milkdown stays out of the main chunk.
|
|
128
|
+
*/
|
|
129
|
+
export async function createCrepeEditor(
|
|
130
|
+
options: CreateCrepeEditorOptions,
|
|
131
|
+
): Promise<CrepeEditorHandle> {
|
|
132
|
+
const crepe = new Crepe({
|
|
133
|
+
root: options.root,
|
|
134
|
+
defaultValue: options.defaultValue,
|
|
135
|
+
features: FEATURES,
|
|
136
|
+
featureConfigs: buildFeatureConfigs(options),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
crepe.editor.config((ctx) => {
|
|
140
|
+
applyMarkdownClasses(ctx, options.classes);
|
|
141
|
+
|
|
142
|
+
// Give the contenteditable an accessible role and name. Milkdown may set
|
|
143
|
+
// its own attributes (e.g. `class`), so merge rather than replace.
|
|
144
|
+
ctx.update(editorViewOptionsCtx, (prev) => ({
|
|
145
|
+
...prev,
|
|
146
|
+
attributes: (state) => {
|
|
147
|
+
const base =
|
|
148
|
+
typeof prev.attributes === 'function'
|
|
149
|
+
? prev.attributes(state)
|
|
150
|
+
: prev.attributes;
|
|
151
|
+
return {
|
|
152
|
+
...(base ?? {}),
|
|
153
|
+
role: 'textbox',
|
|
154
|
+
'aria-multiline': 'true',
|
|
155
|
+
...(options.ariaLabel ? { 'aria-label': options.ariaLabel } : {}),
|
|
156
|
+
};
|
|
157
|
+
},
|
|
158
|
+
}));
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
crepe.on((api) => {
|
|
162
|
+
api.markdownUpdated((_ctx, markdown) => {
|
|
163
|
+
options.onMarkdownUpdated(markdown);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Before `create()` this only records the flag; Crepe reads it when it
|
|
168
|
+
// builds the view, so the first render is already non-editable.
|
|
169
|
+
crepe.setReadonly(options.readonly ?? false);
|
|
170
|
+
|
|
171
|
+
await crepe.create();
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
crepe,
|
|
175
|
+
setMarkdown: (markdown) => {
|
|
176
|
+
crepe.editor.action(replaceAll(markdown));
|
|
177
|
+
},
|
|
178
|
+
getMarkdown: () => crepe.getMarkdown(),
|
|
179
|
+
setReadonly: (readonly) => {
|
|
180
|
+
crepe.setReadonly(readonly);
|
|
181
|
+
},
|
|
182
|
+
focus: () => {
|
|
183
|
+
crepe.editor.action((ctx) => {
|
|
184
|
+
ctx.get(editorViewCtx).focus();
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
destroy: () => {
|
|
188
|
+
// Teardown is best-effort; a rejection here has nothing to report to.
|
|
189
|
+
crepe.destroy().catch(() => undefined);
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline SVG strings for the Crepe feature configs (toolbar, link tooltip,
|
|
3
|
+
* list markers, code block tools). Crepe expects raw markup, not components,
|
|
4
|
+
* so these mirror the Lucide icons ui-library's `UiIcon` renders — same
|
|
5
|
+
* 24×24 grid, 2px round strokes — to keep the editor visually consistent.
|
|
6
|
+
*
|
|
7
|
+
* Each shape carries its own `fill`/`stroke` presentation attributes because
|
|
8
|
+
* Crepe's stylesheet sets `fill` on the `<svg>` element, which would otherwise
|
|
9
|
+
* inherit into the paths and blot out stroke-only icons.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const STROKE_ATTRS =
|
|
13
|
+
'fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"';
|
|
14
|
+
|
|
15
|
+
function strokeIcon(shapes: string[]): string {
|
|
16
|
+
return `<svg class="ck-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" focusable="false">${shapes
|
|
17
|
+
.map((shape) => shape.replace('/>', ` ${STROKE_ATTRS}/>`))
|
|
18
|
+
.join('')}</svg>`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const crepeIcons = {
|
|
22
|
+
bold: strokeIcon([
|
|
23
|
+
'<path d="M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8"/>',
|
|
24
|
+
]),
|
|
25
|
+
italic: strokeIcon([
|
|
26
|
+
'<line x1="19" x2="10" y1="4" y2="4"/>',
|
|
27
|
+
'<line x1="14" x2="5" y1="20" y2="20"/>',
|
|
28
|
+
'<line x1="15" x2="9" y1="4" y2="20"/>',
|
|
29
|
+
]),
|
|
30
|
+
strikethrough: strokeIcon([
|
|
31
|
+
'<path d="M16 4H9a3 3 0 0 0-2.83 4"/>',
|
|
32
|
+
'<path d="M14 12a4 4 0 0 1 0 8H6"/>',
|
|
33
|
+
'<line x1="4" x2="20" y1="12" y2="12"/>',
|
|
34
|
+
]),
|
|
35
|
+
code: strokeIcon([
|
|
36
|
+
'<polyline points="16 18 22 12 16 6"/>',
|
|
37
|
+
'<polyline points="8 6 2 12 8 18"/>',
|
|
38
|
+
]),
|
|
39
|
+
link: strokeIcon([
|
|
40
|
+
'<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>',
|
|
41
|
+
'<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>',
|
|
42
|
+
]),
|
|
43
|
+
pencil: strokeIcon([
|
|
44
|
+
'<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/>',
|
|
45
|
+
'<path d="m15 5 4 4"/>',
|
|
46
|
+
]),
|
|
47
|
+
trash: strokeIcon([
|
|
48
|
+
'<path d="M3 6h18"/>',
|
|
49
|
+
'<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/>',
|
|
50
|
+
'<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/>',
|
|
51
|
+
'<line x1="10" x2="10" y1="11" y2="17"/>',
|
|
52
|
+
'<line x1="14" x2="14" y1="11" y2="17"/>',
|
|
53
|
+
]),
|
|
54
|
+
check: strokeIcon(['<path d="M20 6 9 17l-5-5"/>']),
|
|
55
|
+
copy: strokeIcon([
|
|
56
|
+
'<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/>',
|
|
57
|
+
'<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>',
|
|
58
|
+
]),
|
|
59
|
+
close: strokeIcon(['<path d="M18 6 6 18"/>', '<path d="m6 6 12 12"/>']),
|
|
60
|
+
square: strokeIcon(['<rect width="18" height="18" x="3" y="3" rx="2"/>']),
|
|
61
|
+
squareCheck: strokeIcon([
|
|
62
|
+
'<rect width="18" height="18" x="3" y="3" rx="2"/>',
|
|
63
|
+
'<path d="m9 12 2 2 4-4"/>',
|
|
64
|
+
]),
|
|
65
|
+
chevronDown: strokeIcon(['<path d="m6 9 6 6 6-6"/>']),
|
|
66
|
+
search: strokeIcon([
|
|
67
|
+
'<circle cx="11" cy="11" r="8"/>',
|
|
68
|
+
'<path d="m21 21-4.3-4.3"/>',
|
|
69
|
+
]),
|
|
70
|
+
/**
|
|
71
|
+
* Bullet marker approximating the browser's `list-style: disc` that
|
|
72
|
+
* `CkMarkdownRenderer` gets from the `list-disc` utility: a filled dot in the
|
|
73
|
+
* text colour.
|
|
74
|
+
*/
|
|
75
|
+
bullet:
|
|
76
|
+
'<svg class="ck-icon ck-icon--bullet" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="3" fill="currentColor"/></svg>',
|
|
77
|
+
} as const;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Ctx } from '@milkdown/kit/ctx';
|
|
2
|
+
import {
|
|
3
|
+
blockquoteAttr,
|
|
4
|
+
bulletListAttr,
|
|
5
|
+
codeBlockAttr,
|
|
6
|
+
emphasisAttr,
|
|
7
|
+
headingAttr,
|
|
8
|
+
hrAttr,
|
|
9
|
+
imageAttr,
|
|
10
|
+
inlineCodeAttr,
|
|
11
|
+
linkAttr,
|
|
12
|
+
listItemAttr,
|
|
13
|
+
orderedListAttr,
|
|
14
|
+
paragraphAttr,
|
|
15
|
+
strongAttr,
|
|
16
|
+
} from '@milkdown/kit/preset/commonmark';
|
|
17
|
+
import { strikethroughAttr } from '@milkdown/kit/preset/gfm';
|
|
18
|
+
import type { CkMarkdownElementClasses } from '../CkMarkdownRenderer';
|
|
19
|
+
|
|
20
|
+
type HeadingTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
21
|
+
|
|
22
|
+
/** Minimal shape of the Milkdown `ctx` this module needs; eases unit testing. */
|
|
23
|
+
export type AttrCtx = Pick<Ctx, 'set'>;
|
|
24
|
+
|
|
25
|
+
function toAttrs(className: string | undefined): Record<string, string> {
|
|
26
|
+
return className ? { class: className } : {};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Feed the per-tag class map that `CkMarkdownRenderer` uses into Milkdown's
|
|
31
|
+
* node/mark attribute hooks, so the editor emits the same class names on the
|
|
32
|
+
* same elements. Must run inside `editor.config()` before the editor is
|
|
33
|
+
* created.
|
|
34
|
+
*
|
|
35
|
+
* `li` and `pre` are Crepe node views (list item block, CodeMirror block); the
|
|
36
|
+
* hooks below still register for them so a non-Crepe render path keeps parity,
|
|
37
|
+
* but inside `CkMilkdownEditor` those two are styled by `milkdown-theme.css`.
|
|
38
|
+
*/
|
|
39
|
+
export function applyMarkdownClasses(
|
|
40
|
+
ctx: AttrCtx,
|
|
41
|
+
classes: CkMarkdownElementClasses,
|
|
42
|
+
): void {
|
|
43
|
+
ctx.set(paragraphAttr.key, () => toAttrs(classes.p));
|
|
44
|
+
ctx.set(headingAttr.key, (node) => {
|
|
45
|
+
const tag = `h${node.attrs.level as number}` as HeadingTag;
|
|
46
|
+
return toAttrs(classes[tag]);
|
|
47
|
+
});
|
|
48
|
+
ctx.set(blockquoteAttr.key, () => toAttrs(classes.blockquote));
|
|
49
|
+
ctx.set(hrAttr.key, () => toAttrs(classes.hr));
|
|
50
|
+
ctx.set(imageAttr.key, () => toAttrs(classes.img));
|
|
51
|
+
ctx.set(bulletListAttr.key, () => toAttrs(classes.ul));
|
|
52
|
+
ctx.set(orderedListAttr.key, () => toAttrs(classes.ol));
|
|
53
|
+
ctx.set(listItemAttr.key, () => toAttrs(classes.li));
|
|
54
|
+
// The code block hook returns attributes for both generated elements.
|
|
55
|
+
ctx.set(codeBlockAttr.key, () => ({
|
|
56
|
+
pre: toAttrs(classes.pre),
|
|
57
|
+
code: toAttrs(classes.code),
|
|
58
|
+
}));
|
|
59
|
+
ctx.set(inlineCodeAttr.key, () => toAttrs(classes.code));
|
|
60
|
+
ctx.set(linkAttr.key, () => toAttrs(classes.a));
|
|
61
|
+
ctx.set(strongAttr.key, () => toAttrs(classes.strong));
|
|
62
|
+
ctx.set(emphasisAttr.key, () => toAttrs(classes.em));
|
|
63
|
+
ctx.set(strikethroughAttr.key, () => toAttrs(classes.del));
|
|
64
|
+
}
|