@lostgradient/cinder 0.7.0 → 0.9.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/CHANGELOG.md +23 -0
- package/README.md +7 -2
- package/components.json +59 -15
- package/dist/components/_internal/command-list-context.d.ts +3 -3
- package/dist/components/_internal/create-command-list-state.svelte.d.ts +11 -11
- package/dist/components/access-gate/index.js +201 -126
- package/dist/components/approval-card/approval-card-actions.svelte.d.ts +16 -0
- package/dist/components/approval-card/approval-card-state.d.ts +65 -0
- package/dist/components/approval-card/approval-card.css +269 -92
- package/dist/components/approval-card/approval-card.schema.js +10 -31
- package/dist/components/approval-card/approval-card.svelte.d.ts +3 -3
- package/dist/components/approval-card/approval-card.types.d.ts +14 -28
- package/dist/components/approval-card/index.d.ts +1 -1
- package/dist/components/approval-card/index.js +4268 -2884
- package/dist/components/card/card.css +5 -0
- package/dist/components/card/card.schema.js +2 -2
- package/dist/components/card/card.types.d.ts +2 -2
- package/dist/components/card/index.js +2 -1
- package/dist/components/chat/chat.schema.js +48 -1
- package/dist/components/chat/chat.svelte.d.ts +6 -2
- package/dist/components/chat/chat.types.d.ts +37 -0
- package/dist/components/chat/container/chat.svelte.d.ts +6 -2
- package/dist/components/chat/index.d.ts +3 -3
- package/dist/components/chat/index.js +1099 -4865
- package/dist/components/chat/input/chat-attachment-serialization.d.ts +9 -0
- package/dist/components/chat/input/chat-input.svelte.d.ts +24 -0
- package/dist/components/chat/input/index.d.ts +1 -0
- package/dist/components/chat-composer-popover/chat-composer-popover-filter.d.ts +11 -0
- package/dist/components/chat-composer-popover/chat-composer-popover.css +7 -0
- package/dist/components/chat-composer-popover/chat-composer-popover.css.d.ts +1 -0
- package/dist/components/chat-composer-popover/chat-composer-popover.schema.d.ts +3 -0
- package/dist/components/chat-composer-popover/chat-composer-popover.schema.js +150 -0
- package/dist/components/chat-composer-popover/chat-composer-popover.svelte.d.ts +46 -0
- package/dist/components/chat-composer-popover/chat-composer-popover.types.d.ts +77 -0
- package/dist/components/chat-composer-popover/chat-composer-popover.variables.d.ts +2 -0
- package/dist/components/chat-composer-popover/chat-composer-popover.variables.js +61 -0
- package/dist/components/chat-composer-popover/index.d.ts +6 -0
- package/dist/components/chat-composer-popover/index.js +3285 -0
- package/dist/components/chat-conversation-header/index.js +1627 -5600
- package/dist/components/chat-conversation-list/index.js +15 -3988
- package/dist/components/code-block/code-block.schema.js +5 -1
- package/dist/components/code-block/code-block.types.d.ts +6 -0
- package/dist/components/code-block/index.js +4 -4
- package/dist/components/command-item/index.js +7 -6
- package/dist/components/command-menu/command-menu.schema.js +5 -1
- package/dist/components/command-menu/command-menu.types.d.ts +2 -0
- package/dist/components/command-menu/index.js +48 -19
- package/dist/components/command-palette/index.js +40 -14
- package/dist/components/event-stream-viewer/index.js +44 -44
- package/dist/components/experimental/json-viewer/index.js +44 -44
- package/dist/components/json-schema-editor/index.js +5 -2
- package/dist/components/json-viewer/index.js +44 -44
- package/dist/components/json-viewer/json-viewer.css +10 -6
- package/dist/components/navigation-bar/index.js +44 -6
- package/dist/components/payload-inspector/index.d.ts +1 -1
- package/dist/components/payload-inspector/index.js +306 -1422
- package/dist/components/payload-inspector/payload-inspector.css +15 -75
- package/dist/components/payload-inspector/payload-inspector.schema.js +5 -41
- package/dist/components/payload-inspector/payload-inspector.svelte.d.ts +5 -5
- package/dist/components/payload-inspector/payload-inspector.types.d.ts +10 -56
- package/dist/components/sidebar/index.js +6 -3
- package/dist/components/sidebar/sidebar.css +4 -0
- package/dist/components/tab/index.js +5 -2
- package/dist/components/tab/tab.schema.js +7 -3
- package/dist/components/tab/tab.svelte.d.ts +2 -1
- package/dist/components/tab/tab.types.d.ts +8 -2
- package/dist/components/tabs/index.js +5 -2
- package/dist/components/team-section/index.js +2 -1
- package/dist/components/testimonial-section/index.js +2 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +24169 -27821
- package/dist/server/components/access-gate/index.js +2 -1
- package/dist/server/components/accordion/index.js +2 -2
- package/dist/server/components/alert-dialog/index.js +2 -2
- package/dist/server/components/approval-card/index.js +15 -19
- package/dist/server/components/autocomplete/index.js +7 -7
- package/dist/server/components/avatar-group/index.js +5 -5
- package/dist/server/components/button-group/index.js +3 -5
- package/dist/server/components/card/index.js +2 -2
- package/dist/server/components/chat/index.js +14 -11
- package/dist/server/components/chat-composer-popover/index.js +26 -0
- package/dist/server/components/chat-conversation-header/index.js +9 -10
- package/dist/server/components/chat-conversation-list/index.js +4 -5
- package/dist/server/components/checkbox/index.js +3 -3
- package/dist/server/components/choice-grid/index.js +2 -2
- package/dist/server/components/code-block/index.js +2 -2
- package/dist/server/components/color-field/index.js +3 -3
- package/dist/server/components/combobox/index.js +6 -6
- package/dist/server/components/command-item/index.js +5 -3
- package/dist/server/components/command-menu/index.js +6 -4
- package/dist/server/components/command-palette/index.js +2 -2
- package/dist/server/components/confirm-dialog/index.js +2 -2
- package/dist/server/components/context-menu/index.js +5 -5
- package/dist/server/components/data-table/index.js +5 -5
- package/dist/server/components/date-picker/index.js +6 -6
- package/dist/server/components/date-range-field/index.js +7 -7
- package/dist/server/components/description-list/index.js +3 -5
- package/dist/server/components/diff-viewer/index.js +7 -7
- package/dist/server/components/drawer/index.js +3 -3
- package/dist/server/components/dropdown/index.js +4 -4
- package/dist/server/components/event-stream-viewer/index.js +2 -2
- package/dist/server/components/experimental/json-viewer/index.js +2 -2
- package/dist/server/components/faceted-filter-bar/index.js +1 -1
- package/dist/server/components/hover-card/index.js +1 -1
- package/dist/server/components/input/index.js +3 -3
- package/dist/server/components/json-schema-editor/index.js +22 -22
- package/dist/server/components/json-viewer/index.js +2 -2
- package/dist/server/components/markdown-editor/index.js +13 -13
- package/dist/server/components/modal/index.js +1 -1
- package/dist/server/components/multi-select/index.js +1 -1
- package/dist/server/components/navigation-bar/index.js +1 -1
- package/dist/server/components/newsletter-section/index.js +3 -3
- package/dist/server/components/payload-inspector/index.js +4 -16
- package/dist/server/components/popover/index.js +5 -5
- package/dist/server/components/review-editor/index.js +25 -25
- package/dist/server/components/run-step-timeline/index.js +2 -2
- package/dist/server/components/schema-form/index.js +4 -4
- package/dist/server/components/secret-value-field/index.js +3 -5
- package/dist/server/components/segment/index.js +3 -3
- package/dist/server/components/segmented-control/index.js +4 -4
- package/dist/server/components/selection-popover/index.js +4 -4
- package/dist/server/components/sheet/index.js +2 -2
- package/dist/server/components/sidebar/index.js +3 -3
- package/dist/server/components/tab/index.js +2 -2
- package/dist/server/components/table/index.js +3 -3
- package/dist/server/components/tabs/index.js +4 -4
- package/dist/server/components/tag-input/index.js +3 -3
- package/dist/server/components/team-section/index.js +8 -8
- package/dist/server/components/testimonial-section/index.js +2 -2
- package/dist/server/components/toggle/index.js +2 -2
- package/dist/server/components/tooltip/index.js +1 -1
- package/dist/server/index.js +160 -149
- package/dist/server/{index.server-cbtg8rq5.js → index.server-0atyznp7.js} +5 -5
- package/dist/server/{index.server-20y4mdhm.js → index.server-0chhpkg7.js} +40 -14
- package/dist/server/{index.server-ecm1vs3g.js → index.server-1nq7z1qw.js} +1 -1
- package/dist/server/{index.server-jync915v.js → index.server-2dga5yzr.js} +25 -25
- package/dist/server/index.server-2hqrv7ca.js +9 -0
- package/dist/server/{index.server-qwknp759.js → index.server-2qj6s2mh.js} +7 -4
- package/dist/server/{index.server-pt8b65mt.js → index.server-38aywdk1.js} +1 -1
- package/dist/server/{index.server-rermas0k.js → index.server-47v2fhp4.js} +4 -4
- package/dist/server/{index.server-9j5sym59.js → index.server-4x91gzy1.js} +2 -2
- package/dist/server/{index.server-q48q15g7.js → index.server-4y2jtvv1.js} +4 -2
- package/dist/server/{index.server-zeqv7nrz.js → index.server-5tf22d6e.js} +43 -6
- package/dist/server/index.server-67qgdypc.js +9 -0
- package/dist/server/{index.server-9jete4xx.js → index.server-6sh11ap6.js} +4 -4
- package/dist/server/{index.server-4ztq565m.js → index.server-85p4ydph.js} +1 -1
- package/dist/server/{index.server-4nk7gy0q.js → index.server-894t97kv.js} +1 -1
- package/dist/server/{index.server-1vscnped.js → index.server-8dt36sdm.js} +2 -1
- package/dist/server/{index.server-bfsbp9dz.js → index.server-8xvc73zh.js} +4 -4
- package/dist/server/{index.server-cvnw3hw9.js → index.server-9kz37z89.js} +1 -1
- package/dist/server/{index.server-kc1k02tk.js → index.server-9ykk1kbg.js} +1 -1
- package/dist/server/{index.server-g789yep6.js → index.server-aqethy0w.js} +2 -2
- package/dist/server/{index.server-wc9aggfz.js → index.server-bmxbsk4y.js} +7 -7
- package/dist/server/{index.server-j3ddzcaa.js → index.server-c6pe4kaf.js} +6 -6
- package/dist/server/{index.server-q89dz8h1.js → index.server-d3pqthtp.js} +4 -3
- package/dist/server/{index.server-qfa2rxzx.js → index.server-dg83ke3k.js} +3 -5
- package/dist/server/{index.server-jk4rx4aa.js → index.server-dmmvwy6p.js} +1 -1
- package/dist/server/{index.server-hcfnae5y.js → index.server-dwwqssey.js} +4 -4
- package/dist/server/{index.server-e8x1hgw6.js → index.server-dz1r9xw5.js} +4 -4
- package/dist/server/{index.server-wm7mk5kw.js → index.server-ez5hp0ya.js} +1 -1
- package/dist/server/{index.server-ejyf7e48.js → index.server-fy068yne.js} +1 -1
- package/dist/server/{index.server-wc8dbm5e.js → index.server-g95bk8rc.js} +4 -2
- package/dist/server/index.server-gkazv439.js +576 -0
- package/dist/server/{index.server-dp9j2kkr.js → index.server-h0cvwvhy.js} +1 -1
- package/dist/server/{index.server-pacrqvyz.js → index.server-h90pjrez.js} +1 -1
- package/dist/server/{index.server-p8d04em5.js → index.server-hcsd5gdb.js} +6 -2
- package/dist/server/{index.server-n39drzes.js → index.server-hpeavvnq.js} +1 -1
- package/dist/server/{index.server-1p3patnj.js → index.server-hx91ng7z.js} +1 -1
- package/dist/server/{index.server-jtj3r53y.js → index.server-jgb8yh5k.js} +1 -1
- package/dist/server/{index.server-w1g5q9j9.js → index.server-jtgw9d23.js} +4 -2
- package/dist/server/{index.server-mehav5vs.js → index.server-jxg42jpz.js} +4 -4
- package/dist/server/{index.server-hd8gpehd.js → index.server-jxseyx33.js} +1 -1
- package/dist/server/{index.server-kg2sepsr.js → index.server-krqvnt6p.js} +14 -15
- package/dist/server/{index.server-0e1m3agz.js → index.server-ks49bprh.js} +1 -1
- package/dist/server/{index.server-f0hma3jp.js → index.server-kt0ncrqs.js} +1 -1
- package/dist/server/index.server-m0ezemav.js +170 -0
- package/dist/server/index.server-m191rm44.js +79 -0
- package/dist/server/{index.server-f23vtaw4.js → index.server-marjz8ps.js} +6 -6
- package/dist/server/index.server-mqt1ngqw.js +250 -0
- package/dist/server/{index.server-s9khh4ry.js → index.server-mzzb7dnm.js} +3 -5
- package/dist/server/{index.server-z721a5gp.js → index.server-n82rcv0m.js} +4 -4
- package/dist/server/{index.server-rhhmdxtz.js → index.server-np3c7kx0.js} +4 -4
- package/dist/server/{index.server-hpcs7tmh.js → index.server-p56xxef1.js} +1 -1
- package/dist/server/{index.server-w83kmcrx.js → index.server-pag862vz.js} +10 -10
- package/dist/server/{index.server-gxf0d1p5.js → index.server-q7w4jw89.js} +2 -2
- package/dist/server/{index.server-bvmwnjx6.js → index.server-qh9qzd5t.js} +186 -23
- package/dist/server/{index.server-pxg74pyp.js → index.server-qk1h0n02.js} +4 -4
- package/dist/server/{index.server-sh2rxejg.js → index.server-qke3ekjt.js} +1 -1
- package/dist/server/{index.server-73x2d3j3.js → index.server-rbpvcrr3.js} +1 -1
- package/dist/server/{index.server-8cxs4kya.js → index.server-rga8sc7j.js} +41 -44
- package/dist/server/{index.server-e3fnv1g6.js → index.server-sedjxt36.js} +5 -5
- package/dist/server/{index.server-pen9pxt6.js → index.server-sf3nk3j2.js} +2 -2
- package/dist/server/{index.server-7fp0epev.js → index.server-veastatv.js} +4 -4
- package/dist/server/{index.server-jeqg2d22.js → index.server-xeqtpv61.js} +1 -1
- package/dist/server/{index.server-9s4na701.js → index.server-xjnge1yw.js} +4 -4
- package/dist/server/index.server-xtjjmsyq.js +315 -0
- package/dist/server/{index.server-hhxjaeen.js → index.server-xz99aed4.js} +4 -4
- package/dist/server/{index.server-vne4rzfa.js → index.server-y4n8vc08.js} +1 -1
- package/dist/server/{index.server-5d01cn4f.js → index.server-y83kqayt.js} +4 -4
- package/dist/server/{index.server-1nh09ck7.js → index.server-yhzydqxe.js} +5 -5
- package/dist/server/{index.server-atjc1qcq.js → index.server-yz4k36cw.js} +1 -1
- package/dist/server/{index.server-w6k62afr.js → index.server-ze1ymmbw.js} +2 -2
- package/dist/server/{index.server-914v805w.js → index.server-zgd1g8mk.js} +1 -1
- package/dist/server/{index.server-aeexcxdp.js → index.server-zh3r8vfd.js} +1 -1
- package/dist/server/{index.server-10jb2jzm.js → index.server-zsjf8enc.js} +4 -4
- package/package.json +42 -6
- package/src/components/_internal/command-list-context.ts +3 -3
- package/src/components/_internal/create-command-list-state.svelte.ts +39 -13
- package/src/components/approval-card/approval-card-actions.svelte +168 -0
- package/src/components/approval-card/approval-card-state.ts +152 -1
- package/src/components/approval-card/approval-card.css +269 -92
- package/src/components/approval-card/approval-card.examples.json +3 -3
- package/src/components/approval-card/approval-card.schema.json +9 -30
- package/src/components/approval-card/approval-card.schema.ts +13 -31
- package/src/components/approval-card/approval-card.svelte +176 -470
- package/src/components/approval-card/approval-card.types.ts +15 -28
- package/src/components/approval-card/index.ts +1 -0
- package/src/components/card/card.css +5 -0
- package/src/components/card/card.schema.json +1 -1
- package/src/components/card/card.schema.ts +1 -1
- package/src/components/card/card.svelte +1 -0
- package/src/components/card/card.types.ts +2 -2
- package/src/components/chat/chat.schema.json +40 -0
- package/src/components/chat/chat.schema.ts +49 -0
- package/src/components/chat/chat.svelte +20 -0
- package/src/components/chat/chat.types.ts +38 -0
- package/src/components/chat/container/chat.svelte +82 -3
- package/src/components/chat/index.ts +15 -3
- package/src/components/chat/input/chat-attachment-serialization.ts +63 -0
- package/src/components/chat/input/chat-input.svelte +30 -3
- package/src/components/chat/input/index.ts +5 -0
- package/src/components/chat-composer-popover/chat-composer-popover-filter.ts +68 -0
- package/src/components/chat-composer-popover/chat-composer-popover.css +7 -0
- package/src/components/chat-composer-popover/chat-composer-popover.examples.json +13 -0
- package/src/components/chat-composer-popover/chat-composer-popover.schema.json +90 -0
- package/src/components/chat-composer-popover/chat-composer-popover.schema.ts +95 -0
- package/src/components/chat-composer-popover/chat-composer-popover.svelte +234 -0
- package/src/components/chat-composer-popover/chat-composer-popover.types.ts +88 -0
- package/src/components/chat-composer-popover/chat-composer-popover.variables.json +1 -0
- package/src/components/chat-composer-popover/chat-composer-popover.variables.ts +3 -0
- package/src/components/chat-composer-popover/index.ts +19 -0
- package/src/components/code-block/code-block.schema.json +4 -0
- package/src/components/code-block/code-block.schema.ts +5 -0
- package/src/components/code-block/code-block.svelte +3 -3
- package/src/components/code-block/code-block.types.ts +6 -0
- package/src/components/command-item/command-item.svelte +7 -6
- package/src/components/command-menu/command-menu.schema.json +4 -0
- package/src/components/command-menu/command-menu.schema.ts +4 -0
- package/src/components/command-menu/command-menu.svelte +16 -5
- package/src/components/command-menu/command-menu.types.ts +2 -0
- package/src/components/json-viewer/_json-viewer-node.svelte +15 -1
- package/src/components/json-viewer/json-viewer.css +10 -6
- package/src/components/navigation-bar/navigation-bar.svelte +49 -7
- package/src/components/payload-inspector/index.ts +0 -2
- package/src/components/payload-inspector/payload-inspector.css +15 -75
- package/src/components/payload-inspector/payload-inspector.examples.json +5 -5
- package/src/components/payload-inspector/payload-inspector.schema.json +3 -31
- package/src/components/payload-inspector/payload-inspector.schema.ts +3 -36
- package/src/components/payload-inspector/payload-inspector.svelte +46 -239
- package/src/components/payload-inspector/payload-inspector.types.ts +10 -58
- package/src/components/sidebar/sidebar.css +4 -0
- package/src/components/sidebar/sidebar.svelte +6 -3
- package/src/components/tab/tab.schema.json +6 -2
- package/src/components/tab/tab.schema.ts +8 -2
- package/src/components/tab/tab.svelte +9 -5
- package/src/components/tab/tab.types.ts +8 -2
- package/src/index.ts +18 -5
- package/src/styles/components.css +1 -0
- package/dist/server/index-49we2xfs.js +0 -344
- package/dist/server/index-8fhvxk30.js +0 -265
- package/dist/server/index-x2ysg8ds.js +0 -388
- package/dist/server/index.server-20j0jh30.js +0 -9
- package/dist/server/index.server-dc35kh7d.js +0 -1835
- package/dist/server/index.server-kd4b0342.js +0 -607
- package/dist/server/index.server-n5cdmsz4.js +0 -30
- package/dist/server/index.server-rjap4k7y.js +0 -1389
- package/dist/server/index.server-vt6sn9y4.js +0 -447
- package/dist/server/index.server-w63n618v.js +0 -9
- package/dist/server/index.server-y2pa1k46.js +0 -9
|
@@ -40,7 +40,7 @@ const attachmentIdFactory = createIdFactory('attachment');
|
|
|
40
40
|
// Tracks hash-derived IDs used in this instance so collisions (same name/size/mtime)
|
|
41
41
|
// get a unique suffix rather than a duplicate DOM ID.
|
|
42
42
|
const usedAttachmentIds = new Set();
|
|
43
|
-
let { id, value = $bindable(''), placeholder = 'Type a message...', composerLabel = 'Message', disabled = false, sending = false, error, class: className, action, name = 'content', clearOnSubmit = true, allowAttachments = true, maxFileSize = 10 * 1024 * 1024, // 10MB
|
|
43
|
+
let { id, value = $bindable(''), placeholder = 'Type a message...', composerLabel = 'Message', composerRole, composerAriaExpanded, composerAriaControls, composerAriaActiveDescendant, composerAriaAutocomplete, disabled = false, sending = false, error, class: className, action, name = 'content', clearOnSubmit = true, allowAttachments = true, maxFileSize = 10 * 1024 * 1024, // 10MB
|
|
44
44
|
acceptedTypes = [
|
|
45
45
|
'image/*',
|
|
46
46
|
'text/*',
|
|
@@ -55,7 +55,7 @@ acceptedTypes = [
|
|
|
55
55
|
'application/x-sh',
|
|
56
56
|
'application/sql',
|
|
57
57
|
'application/toml',
|
|
58
|
-
], onsubmit, onstop, onattachmentadd, onattachmentremove, onattachmentfailure, actions, ...rest } = $props();
|
|
58
|
+
], onsubmit, onstop, oncomposerinput, oncomposerkeydown, oncomposerselectionchange, oncomposerblur, onattachmentadd, onattachmentremove, onattachmentfailure, actions, ...rest } = $props();
|
|
59
59
|
const resolvedComposerLabel = $derived(composerLabel.trim() || 'Message');
|
|
60
60
|
// Derived: show stop button when sending and onstop is provided
|
|
61
61
|
const showStopButton = $derived(sending && onstop !== undefined);
|
|
@@ -266,6 +266,12 @@ function handleKeyDown(event) {
|
|
|
266
266
|
if (event.isComposing || isComposing) {
|
|
267
267
|
return;
|
|
268
268
|
}
|
|
269
|
+
if (event.target === editorElement) {
|
|
270
|
+
oncomposerkeydown?.(event);
|
|
271
|
+
if (event.defaultPrevented) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
269
275
|
// Shift+Enter: Newline (let editor handle it)
|
|
270
276
|
if (event.key === 'Enter' && event.shiftKey) {
|
|
271
277
|
return;
|
|
@@ -284,6 +290,12 @@ function handleCompositionStart() {
|
|
|
284
290
|
function handleCompositionEnd() {
|
|
285
291
|
isComposing = false;
|
|
286
292
|
}
|
|
293
|
+
// Fires after `bind:value` has already applied the textarea's new value
|
|
294
|
+
// (Svelte merges the binding's own input listener with this handler on the
|
|
295
|
+
// same event), so `value` here is always current — never one keystroke stale.
|
|
296
|
+
function handleInput(event) {
|
|
297
|
+
oncomposerinput?.(value, event);
|
|
298
|
+
}
|
|
287
299
|
// =========================================================================
|
|
288
300
|
// Imperative API
|
|
289
301
|
// =========================================================================
|
|
@@ -298,6 +310,12 @@ export function clear() {
|
|
|
298
310
|
export function getAttachments() {
|
|
299
311
|
return [...attachments];
|
|
300
312
|
}
|
|
313
|
+
export function getValue() {
|
|
314
|
+
return value;
|
|
315
|
+
}
|
|
316
|
+
export function getEditorElement() {
|
|
317
|
+
return editorElement;
|
|
318
|
+
}
|
|
301
319
|
export function addFiles(files) {
|
|
302
320
|
if (!allowAttachments)
|
|
303
321
|
return;
|
|
@@ -317,7 +335,6 @@ onDestroy(() => {
|
|
|
317
335
|
method="POST"
|
|
318
336
|
{action}
|
|
319
337
|
onsubmit={handleSubmit}
|
|
320
|
-
onkeydown={handleKeyDown}
|
|
321
338
|
onpaste={handlePaste}
|
|
322
339
|
ondrop={handleDrop}
|
|
323
340
|
ondragover={handleDragOver}
|
|
@@ -358,8 +375,18 @@ onDestroy(() => {
|
|
|
358
375
|
bind:this={editorElement}
|
|
359
376
|
id={`${id}-editor`}
|
|
360
377
|
bind:value
|
|
378
|
+
onkeydown={handleKeyDown}
|
|
379
|
+
oninput={handleInput}
|
|
380
|
+
onpointerup={oncomposerselectionchange}
|
|
381
|
+
onselect={oncomposerselectionchange}
|
|
382
|
+
onblur={oncomposerblur}
|
|
361
383
|
{placeholder}
|
|
384
|
+
role={composerRole}
|
|
362
385
|
aria-label={resolvedComposerLabel}
|
|
386
|
+
aria-expanded={composerAriaExpanded}
|
|
387
|
+
aria-controls={composerAriaControls}
|
|
388
|
+
aria-activedescendant={composerAriaActiveDescendant}
|
|
389
|
+
aria-autocomplete={composerAriaAutocomplete}
|
|
363
390
|
{disabled}
|
|
364
391
|
readonly={sending}
|
|
365
392
|
class="chat-input-editor"
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export { deriveAttachmentKind, type AttachmentKind } from './attachment-kind.js';
|
|
2
2
|
export { default as ChatAttachmentPreview } from './chat-attachment-preview.svelte';
|
|
3
|
+
export {
|
|
4
|
+
serializeChatAttachment,
|
|
5
|
+
serializeChatAttachments,
|
|
6
|
+
type SerializedChatAttachment,
|
|
7
|
+
} from './chat-attachment-serialization.ts';
|
|
3
8
|
export type { ChatAttachment } from './chat-attachment.ts';
|
|
4
9
|
export { default as ChatInput } from './chat-input.svelte';
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export type FuzzyFilterItem = {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
keywords?: readonly string[] | undefined;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type FuzzyFilterResult<TItem extends FuzzyFilterItem> = {
|
|
8
|
+
item: TItem;
|
|
9
|
+
score: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function normalize(value: string): string {
|
|
13
|
+
return value.trim().toLowerCase();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function fuzzySubsequenceScore(candidate: string, query: string): number | null {
|
|
17
|
+
const normalizedCandidate = normalize(candidate);
|
|
18
|
+
const normalizedQuery = normalize(query);
|
|
19
|
+
|
|
20
|
+
if (normalizedQuery.length === 0) return 0;
|
|
21
|
+
if (normalizedCandidate.length === 0) return null;
|
|
22
|
+
|
|
23
|
+
let score = 0;
|
|
24
|
+
let searchIndex = 0;
|
|
25
|
+
let previousMatchIndex = -1;
|
|
26
|
+
|
|
27
|
+
for (const character of normalizedQuery) {
|
|
28
|
+
const matchIndex = normalizedCandidate.indexOf(character, searchIndex);
|
|
29
|
+
if (matchIndex === -1) return null;
|
|
30
|
+
|
|
31
|
+
score += matchIndex === previousMatchIndex + 1 ? 4 : 1;
|
|
32
|
+
if (matchIndex === 0) score += 2;
|
|
33
|
+
if (/[\s/_-]/u.test(normalizedCandidate[matchIndex - 1] ?? '')) score += 2;
|
|
34
|
+
|
|
35
|
+
previousMatchIndex = matchIndex;
|
|
36
|
+
searchIndex = matchIndex + 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return score - normalizedCandidate.length * 0.01;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function filterFuzzySubsequence<TItem extends FuzzyFilterItem>(
|
|
43
|
+
items: readonly TItem[],
|
|
44
|
+
query: string,
|
|
45
|
+
): TItem[] {
|
|
46
|
+
const normalizedQuery = normalize(query);
|
|
47
|
+
if (normalizedQuery.length === 0) return [...items];
|
|
48
|
+
|
|
49
|
+
const matchedItems = items
|
|
50
|
+
.map((item, index) => {
|
|
51
|
+
const candidates = [item.label, item.value, ...(item.keywords ?? [])];
|
|
52
|
+
let bestScore: number | null = null;
|
|
53
|
+
|
|
54
|
+
for (const candidate of candidates) {
|
|
55
|
+
const score = fuzzySubsequenceScore(candidate, normalizedQuery);
|
|
56
|
+
if (score !== null && (bestScore === null || score > bestScore)) {
|
|
57
|
+
bestScore = score;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return bestScore === null ? null : { item, index, score: bestScore };
|
|
62
|
+
})
|
|
63
|
+
.filter((result) => result !== null);
|
|
64
|
+
|
|
65
|
+
matchedItems.sort((a, b) => b.score - a.score || a.index - b.index);
|
|
66
|
+
|
|
67
|
+
return matchedItems.map((result) => result.item);
|
|
68
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
@layer cinder.tokens, cinder.foundation, cinder.components, cinder.utilities;
|
|
2
|
+
/* ChatComposerPopover renders CommandMenu and CommandItem internally. Pull in
|
|
3
|
+
* the command menu family so the wrapper is styled through its own styles path. */
|
|
4
|
+
@import '../command-menu/command-menu.css';
|
|
5
|
+
|
|
6
|
+
@layer cinder.components {
|
|
7
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../schemas/examples.schema.json",
|
|
3
|
+
"component": "chat-composer-popover",
|
|
4
|
+
"import": "@lostgradient/cinder/chat-composer-popover",
|
|
5
|
+
"examples": [
|
|
6
|
+
{
|
|
7
|
+
"id": "slash-commands",
|
|
8
|
+
"title": "Slash commands in ChatInput",
|
|
9
|
+
"description": "Wires ChatComposerPopover to ChatInput with consumer-owned command definitions.",
|
|
10
|
+
"code": "<script lang=\"ts\">\n import ChatComposerPopover, {\n type ChatComposerPopoverSelection,\n } from '@lostgradient/cinder/chat-composer-popover';\n import { ChatInput } from '@lostgradient/cinder/chat';\n\n type SlashCommand = {\n value: string;\n label: string;\n description: string;\n insert: string;\n keywords?: string[];\n };\n\n const commands: SlashCommand[] = [\n {\n value: 'help',\n label: 'Help',\n description: 'Show available assistant commands',\n insert: '/help ',\n keywords: ['docs', 'support'],\n },\n {\n value: 'new',\n label: 'New conversation',\n description: 'Start a fresh thread',\n insert: '/new ',\n keywords: ['reset', 'fresh'],\n },\n {\n value: 'tools',\n label: 'Tools',\n description: 'Open tool controls',\n insert: '/tools ',\n keywords: ['integrations', 'actions'],\n },\n {\n value: 'stop',\n label: 'Stop',\n description: 'Stop the current response',\n insert: '/stop',\n keywords: ['cancel', 'halt'],\n },\n ];\n\n let value = $state('');\n let selectedCommand = $state('None');\n\n function handleSelect(selection: ChatComposerPopoverSelection<SlashCommand>) {\n value = `${value.slice(0, selection.range.start)}${selection.item.insert}${value.slice(\n selection.range.end,\n )}`;\n selectedCommand = selection.item.label;\n }\n</script>\n\n<ChatComposerPopover id=\"slash-command\" bind:value items={commands} onselect={handleSelect}>\n {#snippet composer(composerProps)}\n <ChatInput\n id=\"chat\"\n bind:value\n allowAttachments={false}\n placeholder=\"Type / for commands\"\n composerRole={composerProps.composerRole}\n composerAriaExpanded={composerProps.composerAriaExpanded}\n composerAriaControls={composerProps.composerAriaControls}\n composerAriaActiveDescendant={composerProps.composerAriaActiveDescendant}\n composerAriaAutocomplete={composerProps.composerAriaAutocomplete}\n oncomposerinput={composerProps.oncomposerinput}\n oncomposerkeydown={composerProps.oncomposerkeydown}\n oncomposerselectionchange={composerProps.oncomposerselectionchange}\n oncomposerblur={composerProps.oncomposerblur}\n />\n {/snippet}\n</ChatComposerPopover>\n\n<p>Selected: {selectedCommand}</p>\n"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"id": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"description": "Unique identifier used for the listbox and item ids."
|
|
8
|
+
},
|
|
9
|
+
"value": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Current composer value. Bind this to the same value as ChatInput."
|
|
12
|
+
},
|
|
13
|
+
"triggers": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"items": {
|
|
16
|
+
"type": "string"
|
|
17
|
+
},
|
|
18
|
+
"description": "Trigger characters that open the popover. Default `['/', '@']`."
|
|
19
|
+
},
|
|
20
|
+
"label": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "Accessible listbox label. Default `'Composer suggestions'`."
|
|
23
|
+
},
|
|
24
|
+
"placement": {
|
|
25
|
+
"enum": [
|
|
26
|
+
"top",
|
|
27
|
+
"bottom",
|
|
28
|
+
"left",
|
|
29
|
+
"right",
|
|
30
|
+
"top-start",
|
|
31
|
+
"top-end",
|
|
32
|
+
"bottom-start",
|
|
33
|
+
"bottom-end"
|
|
34
|
+
],
|
|
35
|
+
"description": "Caret-relative placement. Default `'bottom-start'`."
|
|
36
|
+
},
|
|
37
|
+
"offset": {
|
|
38
|
+
"type": "number",
|
|
39
|
+
"description": "Distance in px between the caret and popover. Default `6`."
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"additionalProperties": false,
|
|
43
|
+
"required": ["id"],
|
|
44
|
+
"metadata": {
|
|
45
|
+
"unsupportedProps": [
|
|
46
|
+
{
|
|
47
|
+
"name": "composer",
|
|
48
|
+
"reason": "function-or-snippet",
|
|
49
|
+
"required": true,
|
|
50
|
+
"description": "Render the ChatInput or compatible composer with the provided overlay props."
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "detectTrigger",
|
|
54
|
+
"reason": "function-or-snippet",
|
|
55
|
+
"description": "Override trigger detection."
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "empty",
|
|
59
|
+
"reason": "function-or-snippet",
|
|
60
|
+
"description": "Optional empty state rendered when filtering produces no matching rows."
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "filter",
|
|
64
|
+
"reason": "function-or-snippet",
|
|
65
|
+
"description": "Override filtering."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "item",
|
|
69
|
+
"reason": "function-or-snippet",
|
|
70
|
+
"description": "Optional custom row contents. Defaults to the item label and description."
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "items",
|
|
74
|
+
"reason": "generic-type-parameter",
|
|
75
|
+
"required": true,
|
|
76
|
+
"description": "Consumer-owned command or mention definitions."
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"name": "ondismiss",
|
|
80
|
+
"reason": "function-or-snippet",
|
|
81
|
+
"description": "Invoked when Escape, trigger loss, or outside pointerdown dismisses the popover."
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "onselect",
|
|
85
|
+
"reason": "function-or-snippet",
|
|
86
|
+
"description": "Invoked when an enabled item is selected by keyboard or pointer."
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { ComponentSchema } from '../../schema-types';
|
|
2
|
+
|
|
3
|
+
const schema = {
|
|
4
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
5
|
+
type: 'object',
|
|
6
|
+
properties: {
|
|
7
|
+
id: {
|
|
8
|
+
type: 'string',
|
|
9
|
+
description: 'Unique identifier used for the listbox and item ids.',
|
|
10
|
+
},
|
|
11
|
+
value: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
description: 'Current composer value. Bind this to the same value as ChatInput.',
|
|
14
|
+
},
|
|
15
|
+
triggers: {
|
|
16
|
+
type: 'array',
|
|
17
|
+
items: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
},
|
|
20
|
+
description: "Trigger characters that open the popover. Default `['/', '@']`.",
|
|
21
|
+
},
|
|
22
|
+
label: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: "Accessible listbox label. Default `'Composer suggestions'`.",
|
|
25
|
+
},
|
|
26
|
+
placement: {
|
|
27
|
+
enum: [
|
|
28
|
+
'top',
|
|
29
|
+
'bottom',
|
|
30
|
+
'left',
|
|
31
|
+
'right',
|
|
32
|
+
'top-start',
|
|
33
|
+
'top-end',
|
|
34
|
+
'bottom-start',
|
|
35
|
+
'bottom-end',
|
|
36
|
+
],
|
|
37
|
+
description: "Caret-relative placement. Default `'bottom-start'`.",
|
|
38
|
+
},
|
|
39
|
+
offset: {
|
|
40
|
+
type: 'number',
|
|
41
|
+
description: 'Distance in px between the caret and popover. Default `6`.',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
additionalProperties: false,
|
|
45
|
+
required: ['id'],
|
|
46
|
+
metadata: {
|
|
47
|
+
unsupportedProps: [
|
|
48
|
+
{
|
|
49
|
+
name: 'composer',
|
|
50
|
+
reason: 'function-or-snippet',
|
|
51
|
+
required: true,
|
|
52
|
+
description: 'Render the ChatInput or compatible composer with the provided overlay props.',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'detectTrigger',
|
|
56
|
+
reason: 'function-or-snippet',
|
|
57
|
+
description: 'Override trigger detection.',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'empty',
|
|
61
|
+
reason: 'function-or-snippet',
|
|
62
|
+
description: 'Optional empty state rendered when filtering produces no matching rows.',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'filter',
|
|
66
|
+
reason: 'function-or-snippet',
|
|
67
|
+
description: 'Override filtering.',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'item',
|
|
71
|
+
reason: 'function-or-snippet',
|
|
72
|
+
description: 'Optional custom row contents. Defaults to the item label and description.',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'items',
|
|
76
|
+
reason: 'generic-type-parameter',
|
|
77
|
+
required: true,
|
|
78
|
+
description: 'Consumer-owned command or mention definitions.',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'ondismiss',
|
|
82
|
+
reason: 'function-or-snippet',
|
|
83
|
+
description:
|
|
84
|
+
'Invoked when Escape, trigger loss, or outside pointerdown dismisses the popover.',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'onselect',
|
|
88
|
+
reason: 'function-or-snippet',
|
|
89
|
+
description: 'Invoked when an enabled item is selected by keyboard or pointer.',
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
} satisfies ComponentSchema;
|
|
94
|
+
|
|
95
|
+
export default schema as ComponentSchema;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export { filterFuzzySubsequence, fuzzySubsequenceScore, } from './chat-composer-popover-filter.ts';
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts" generics="TItem extends ChatComposerPopoverItem">
|
|
6
|
+
import CommandItem from '../command-item/command-item.svelte';
|
|
7
|
+
import CommandMenu from '../command-menu/command-menu.svelte';
|
|
8
|
+
import { onDestroy } from 'svelte';
|
|
9
|
+
import { detectTrigger as detectCommandTrigger } from '../command-menu/command-menu-trigger.ts';
|
|
10
|
+
import { filterFuzzySubsequence } from './chat-composer-popover-filter.ts';
|
|
11
|
+
let { id, value = $bindable(''), items, triggers = ['/', '@'], label = 'Composer suggestions', placement = 'bottom-start', offset = 6, composer, item, empty, detectTrigger, filter = filterFuzzySubsequence, onselect, ondismiss, } = $props();
|
|
12
|
+
let open = $state(false);
|
|
13
|
+
let anchor = $state(null);
|
|
14
|
+
let caretIndex = $state(0);
|
|
15
|
+
const listboxId = $derived(`${id}-listbox`);
|
|
16
|
+
let activeItemId = $state(null);
|
|
17
|
+
let activeMatch = $state(null);
|
|
18
|
+
let composerSyncTimer = null;
|
|
19
|
+
let lastSyncedValue = $state(value);
|
|
20
|
+
let suppressNextValueSync = false;
|
|
21
|
+
const emptyContent = $derived(empty);
|
|
22
|
+
const query = $derived(activeMatch?.query ?? '');
|
|
23
|
+
const trigger = $derived(activeMatch?.trigger ?? triggers[0] ?? '/');
|
|
24
|
+
const filteredItems = $derived.by(() => {
|
|
25
|
+
if (!activeMatch)
|
|
26
|
+
return [];
|
|
27
|
+
return [...filter(items, activeMatch.query, activeMatch.trigger)];
|
|
28
|
+
});
|
|
29
|
+
const composerProps = $derived({
|
|
30
|
+
composerRole: 'combobox',
|
|
31
|
+
composerAriaExpanded: open,
|
|
32
|
+
composerAriaControls: open ? listboxId : undefined,
|
|
33
|
+
composerAriaActiveDescendant: open ? (activeItemId ?? undefined) : undefined,
|
|
34
|
+
composerAriaAutocomplete: 'list',
|
|
35
|
+
oncomposerinput: handleComposerInput,
|
|
36
|
+
oncomposerkeydown: handleComposerKeydown,
|
|
37
|
+
oncomposerselectionchange: handleComposerSelectionChange,
|
|
38
|
+
oncomposerblur: handleComposerBlur,
|
|
39
|
+
});
|
|
40
|
+
function getComposerElement(event) {
|
|
41
|
+
const target = event.currentTarget ?? event.target;
|
|
42
|
+
if (target instanceof HTMLTextAreaElement || target instanceof HTMLInputElement) {
|
|
43
|
+
return target;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
function findTriggerMatch(text, selectionStart, selectionEnd) {
|
|
48
|
+
if (detectTrigger)
|
|
49
|
+
return detectTrigger(text, selectionStart, selectionEnd);
|
|
50
|
+
for (const triggerChar of triggers) {
|
|
51
|
+
const match = detectCommandTrigger({ text, selectionStart, selectionEnd, triggerChar });
|
|
52
|
+
if (match) {
|
|
53
|
+
return { ...match, trigger: triggerChar };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function updateFromComposer(composerElement, nextValue) {
|
|
59
|
+
if (composerElement)
|
|
60
|
+
anchor = composerElement;
|
|
61
|
+
const selectionStart = composerElement?.selectionStart ?? nextValue.length;
|
|
62
|
+
const selectionEnd = composerElement?.selectionEnd ?? selectionStart;
|
|
63
|
+
lastSyncedValue = nextValue;
|
|
64
|
+
value = nextValue;
|
|
65
|
+
caretIndex = selectionEnd;
|
|
66
|
+
activeMatch = findTriggerMatch(nextValue, selectionStart, selectionEnd);
|
|
67
|
+
const wasOpen = open;
|
|
68
|
+
open = activeMatch !== null && anchor !== null;
|
|
69
|
+
if (!open) {
|
|
70
|
+
activeItemId = null;
|
|
71
|
+
if (wasOpen)
|
|
72
|
+
ondismiss?.();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function dismiss({ restoreFocus = true } = {}) {
|
|
76
|
+
if (!open && !activeMatch && !activeItemId)
|
|
77
|
+
return;
|
|
78
|
+
clearComposerSyncTimer();
|
|
79
|
+
open = false;
|
|
80
|
+
activeItemId = null;
|
|
81
|
+
activeMatch = null;
|
|
82
|
+
if (restoreFocus)
|
|
83
|
+
anchor?.focus();
|
|
84
|
+
ondismiss?.();
|
|
85
|
+
}
|
|
86
|
+
function handleComposerInput(nextValue, event) {
|
|
87
|
+
suppressNextValueSync = false;
|
|
88
|
+
updateFromComposer(event ? getComposerElement(event) : anchor, nextValue);
|
|
89
|
+
}
|
|
90
|
+
function handleComposerSelectionChange(event) {
|
|
91
|
+
suppressNextValueSync = false;
|
|
92
|
+
const composerElement = getComposerElement(event);
|
|
93
|
+
if (!composerElement)
|
|
94
|
+
return;
|
|
95
|
+
syncComposerSelectionAfterNativeNavigation(composerElement);
|
|
96
|
+
}
|
|
97
|
+
function handleComposerBlur(event) {
|
|
98
|
+
if (event.currentTarget === anchor) {
|
|
99
|
+
dismiss({ restoreFocus: false });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function syncComposerSelectionAfterNativeNavigation(composerElement) {
|
|
103
|
+
clearComposerSyncTimer();
|
|
104
|
+
composerSyncTimer = setTimeout(() => {
|
|
105
|
+
composerSyncTimer = null;
|
|
106
|
+
updateFromComposer(composerElement, composerElement.value);
|
|
107
|
+
}, 0);
|
|
108
|
+
}
|
|
109
|
+
function clearComposerSyncTimer() {
|
|
110
|
+
if (composerSyncTimer === null)
|
|
111
|
+
return;
|
|
112
|
+
clearTimeout(composerSyncTimer);
|
|
113
|
+
composerSyncTimer = null;
|
|
114
|
+
}
|
|
115
|
+
function handleComposerKeydown(event) {
|
|
116
|
+
const composerElement = event.currentTarget instanceof HTMLTextAreaElement ||
|
|
117
|
+
event.currentTarget instanceof HTMLInputElement
|
|
118
|
+
? event.currentTarget
|
|
119
|
+
: null;
|
|
120
|
+
if (composerElement) {
|
|
121
|
+
anchor = composerElement;
|
|
122
|
+
caretIndex = composerElement.selectionEnd ?? value.length;
|
|
123
|
+
}
|
|
124
|
+
if (!open || event.isComposing || event.keyCode === 229)
|
|
125
|
+
return;
|
|
126
|
+
if (event.key === 'Escape') {
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
event.stopPropagation();
|
|
129
|
+
dismiss();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const isNavigationKey = event.key === 'ArrowDown' ||
|
|
133
|
+
event.key === 'ArrowUp' ||
|
|
134
|
+
event.key === 'ArrowLeft' ||
|
|
135
|
+
event.key === 'ArrowRight' ||
|
|
136
|
+
event.key === 'Home' ||
|
|
137
|
+
event.key === 'End';
|
|
138
|
+
const isMenuNavigationKey = event.key === 'ArrowDown' ||
|
|
139
|
+
event.key === 'ArrowUp' ||
|
|
140
|
+
event.key === 'Home' ||
|
|
141
|
+
event.key === 'End';
|
|
142
|
+
const isModifiedNavigation = isNavigationKey && (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey);
|
|
143
|
+
if ((isMenuNavigationKey && !isModifiedNavigation) || (event.key === 'Enter' && activeItemId)) {
|
|
144
|
+
event.preventDefault();
|
|
145
|
+
}
|
|
146
|
+
if (composerElement && isNavigationKey && (isModifiedNavigation || !isMenuNavigationKey)) {
|
|
147
|
+
syncComposerSelectionAfterNativeNavigation(composerElement);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
onDestroy(() => {
|
|
151
|
+
clearComposerSyncTimer();
|
|
152
|
+
});
|
|
153
|
+
$effect(() => {
|
|
154
|
+
if (value === lastSyncedValue)
|
|
155
|
+
return;
|
|
156
|
+
if (suppressNextValueSync) {
|
|
157
|
+
suppressNextValueSync = false;
|
|
158
|
+
lastSyncedValue = value;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
updateFromComposer(anchor, value);
|
|
162
|
+
});
|
|
163
|
+
function handleStateChange(state) {
|
|
164
|
+
activeItemId = state.activeItemId;
|
|
165
|
+
}
|
|
166
|
+
function handleSelect(selection) {
|
|
167
|
+
const selectedItem = filteredItems.find((candidate) => candidate.value === selection.value);
|
|
168
|
+
if (!selectedItem || !activeMatch)
|
|
169
|
+
return;
|
|
170
|
+
const detail = {
|
|
171
|
+
item: selectedItem,
|
|
172
|
+
value: selection.value,
|
|
173
|
+
query: activeMatch.query,
|
|
174
|
+
trigger: activeMatch.trigger,
|
|
175
|
+
range: {
|
|
176
|
+
start: activeMatch.start,
|
|
177
|
+
end: activeMatch.end,
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
open = false;
|
|
181
|
+
activeItemId = null;
|
|
182
|
+
activeMatch = null;
|
|
183
|
+
anchor?.focus();
|
|
184
|
+
suppressNextValueSync = true;
|
|
185
|
+
queueMicrotask(() => {
|
|
186
|
+
suppressNextValueSync = false;
|
|
187
|
+
});
|
|
188
|
+
onselect?.(detail);
|
|
189
|
+
}
|
|
190
|
+
</script>
|
|
191
|
+
|
|
192
|
+
{@render composer(composerProps)}
|
|
193
|
+
|
|
194
|
+
<CommandMenu
|
|
195
|
+
bind:open
|
|
196
|
+
{anchor}
|
|
197
|
+
{caretIndex}
|
|
198
|
+
{query}
|
|
199
|
+
{placement}
|
|
200
|
+
{offset}
|
|
201
|
+
{label}
|
|
202
|
+
{listboxId}
|
|
203
|
+
onselect={handleSelect}
|
|
204
|
+
ondismiss={() => dismiss({ restoreFocus: false })}
|
|
205
|
+
onstatechange={handleStateChange}
|
|
206
|
+
>
|
|
207
|
+
{#snippet items()}
|
|
208
|
+
{#each filteredItems as command (command.value)}
|
|
209
|
+
<CommandItem
|
|
210
|
+
value={command.value}
|
|
211
|
+
disabled={command.disabled === true}
|
|
212
|
+
description={item ? '' : (command.description ?? '')}
|
|
213
|
+
accessibleLabel={command.description
|
|
214
|
+
? `${command.label}, ${command.description}`
|
|
215
|
+
: command.label}
|
|
216
|
+
selectionMode="parent"
|
|
217
|
+
>
|
|
218
|
+
{#if item}
|
|
219
|
+
{@render item({ item: command, query, trigger })}
|
|
220
|
+
{:else}
|
|
221
|
+
{command.label}
|
|
222
|
+
{/if}
|
|
223
|
+
</CommandItem>
|
|
224
|
+
{/each}
|
|
225
|
+
{/snippet}
|
|
226
|
+
|
|
227
|
+
{#snippet empty()}
|
|
228
|
+
{#if emptyContent}
|
|
229
|
+
{@render emptyContent()}
|
|
230
|
+
{:else}
|
|
231
|
+
No suggestions
|
|
232
|
+
{/if}
|
|
233
|
+
{/snippet}
|
|
234
|
+
</CommandMenu>
|