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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anentrypoint-design",
3
- "version": "0.0.372",
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",
@@ -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,