anentrypoint-design 0.0.372 → 0.0.373
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/247420.js +21 -21
- package/package.json +1 -1
- package/src/components/chat.js +37 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anentrypoint-design",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.373",
|
|
4
4
|
"description": "247420 design system SDK — webjsx + modified ripple-ui, single-file ESM bundle for reproducible use of the AnEntrypoint design.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/247420.js",
|
package/src/components/chat.js
CHANGED
|
@@ -305,7 +305,7 @@ function ChatComposerElapsed({ streamingSince }) {
|
|
|
305
305
|
});
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
export function ChatComposer({ value, onInput, onSend, onEmoji, onCancel, busy, placeholder = 'message…', disabled, disabledReason, label, context, onPasteFiles, onDropFiles, streamingSince, detectAttachment }) {
|
|
308
|
+
export function ChatComposer({ value, onInput, onSend, onEmoji, onCancel, busy, placeholder = 'message…', disabled, disabledReason, label, context, onPasteFiles, onDropFiles, streamingSince, detectAttachment, mentionFiles }) {
|
|
309
309
|
// Keep a handle to the live textarea so send() reads the actual DOM value
|
|
310
310
|
// (not the possibly-lagging `value` prop) and so we can sync the DOM value
|
|
311
311
|
// only when it genuinely differs — re-applying `value` on every parent
|
|
@@ -317,6 +317,14 @@ export function ChatComposer({ value, onInput, onSend, onEmoji, onCancel, busy,
|
|
|
317
317
|
if (onSend) onSend(v);
|
|
318
318
|
};
|
|
319
319
|
const triggerMatch = EMOJI_TRIGGER_RE.exec(value || '');
|
|
320
|
+
// `@`-file-mention autocomplete: host supplies mentionFiles (a flat list of
|
|
321
|
+
// {path,isDir} entries, or a plain string[] of paths — filterFileEntries
|
|
322
|
+
// normalizes both), we own detection/filtering/insertion. Caret-position-
|
|
323
|
+
// aware (extractAtQuery needs the text BEFORE the caret, not the whole
|
|
324
|
+
// draft) so an "@" earlier in an already-sent-past part of the text doesn't
|
|
325
|
+
// re-trigger after the cursor has moved on.
|
|
326
|
+
const caretPos = taEl ? taEl.selectionStart : (value || '').length;
|
|
327
|
+
const atQuery = mentionFiles ? extractAtQuery((value || '').slice(0, caretPos)) : null;
|
|
320
328
|
// taEl is only assigned by taRef during DOM diffing, which happens AFTER
|
|
321
329
|
// this render function returns — so on first paint of a trigger it is
|
|
322
330
|
// still null here. Fall back to the live DOM textarea from the previous
|
|
@@ -470,6 +478,33 @@ export function ChatComposer({ value, onInput, onSend, onEmoji, onCancel, busy,
|
|
|
470
478
|
onSelect: (ch) => insertEmoji(ch),
|
|
471
479
|
onClose: () => { if (taEl) { const v = taEl.value.replace(EMOJI_TRIGGER_RE, (full, tail) => full.slice(0, full.length - tail.length)); if (onInput) onInput(v); taEl.value = v; taEl.focus(); } },
|
|
472
480
|
}) : null;
|
|
481
|
+
// insertMention: replace the in-progress @token (atQuery.start..caretPos)
|
|
482
|
+
// with the built mention text, mirroring insertEmoji's DOM-authoritative
|
|
483
|
+
// read/write-back so the native undo stack isn't clobbered any more than
|
|
484
|
+
// the existing emoji path already accepts.
|
|
485
|
+
const insertMention = (entry) => {
|
|
486
|
+
const v = (taEl && taEl.value) || value || '';
|
|
487
|
+
if (!atQuery) return;
|
|
488
|
+
// buildAtInsertText returns {text, cursorOffset} — cursorOffset is
|
|
489
|
+
// relative to the start of the inserted text, matching the emoji
|
|
490
|
+
// path's absolute-caret style once added to atQuery.start.
|
|
491
|
+
const { text: insertText, cursorOffset } = buildAtInsertText(entry.path, entry.isDir);
|
|
492
|
+
const next = v.slice(0, atQuery.start) + insertText + v.slice(caretPos);
|
|
493
|
+
if (onInput) onInput(next);
|
|
494
|
+
if (taEl) {
|
|
495
|
+
taEl.value = next;
|
|
496
|
+
taEl.focus();
|
|
497
|
+
const pos = atQuery.start + cursorOffset;
|
|
498
|
+
taEl.selectionStart = taEl.selectionEnd = pos;
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
const mentionEntries = atQuery ? filterFileEntries(mentionFiles, atQuery.query) : [];
|
|
502
|
+
const mentionPicker = atQuery ? CommandPalette({
|
|
503
|
+
open: true,
|
|
504
|
+
items: mentionEntries.map((e) => ({ label: e.path, group: null, icon: e.isDir ? '📁' : null, _entry: e })),
|
|
505
|
+
onSelect: (it) => insertMention(it._entry),
|
|
506
|
+
onClose: () => { if (taEl) { const v = taEl.value.slice(0, atQuery.start) + taEl.value.slice(caretPos); if (onInput) onInput(v); taEl.value = v; taEl.focus(); taEl.selectionStart = taEl.selectionEnd = atQuery.start; } },
|
|
507
|
+
}) : null;
|
|
473
508
|
return h('div', {
|
|
474
509
|
class: 'chat-composer' + (hasDraft ? ' has-draft' : '') + (disabled ? ' is-disabled' : ''),
|
|
475
510
|
// A drop on the composer must NEVER navigate the browser away from the
|
|
@@ -489,6 +524,7 @@ export function ChatComposer({ value, onInput, onSend, onEmoji, onCancel, busy,
|
|
|
489
524
|
},
|
|
490
525
|
contextLine,
|
|
491
526
|
triggerPicker,
|
|
527
|
+
mentionPicker,
|
|
492
528
|
h('textarea', { ref: taRef, placeholder, rows: 1,
|
|
493
529
|
'aria-label': label || (disabled && disabledReason ? 'message input — ' + disabledReason : 'message input'),
|
|
494
530
|
disabled: !!disabled, 'aria-disabled': disabled ? 'true' : null,
|