anentrypoint-design 0.0.230 → 0.0.231

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.230",
3
+ "version": "0.0.231",
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",
@@ -8,6 +8,12 @@ import { isDegraded as isMarkdownDegraded } from '../markdown.js';
8
8
  import { register } from '../debug.js';
9
9
  import { Icon } from './shell.js';
10
10
  import { fmtFileSize } from './files.js';
11
+ import { EmojiPicker } from './overlay-primitives.js';
12
+
13
+ // Matches a trailing `:keyword` at the end of the composer draft (optionally
14
+ // preceded by whitespace/start-of-string) so typing `:smile` opens an inline
15
+ // filtered EmojiPicker without requiring the toolbar button or Ctrl+;.
16
+ const EMOJI_TRIGGER_RE = /(?:^|\s)(:([a-zA-Z0-9_+-]{0,24}))$/;
11
17
 
12
18
  const h = webjsx.createElement;
13
19
  let _stats = { messages: 0, lastKindCounts: {} };
@@ -447,6 +453,24 @@ export function ChatComposer({ value, onInput, onSend, onAttach, onEmoji, onMenu
447
453
  if (!v || disabled) return;
448
454
  if (onSend) onSend(v);
449
455
  };
456
+ const triggerMatch = EMOJI_TRIGGER_RE.exec(value || '');
457
+ // taEl is only assigned by taRef during DOM diffing, which happens AFTER
458
+ // this render function returns — so on first paint of a trigger it is
459
+ // still null here. Fall back to the live DOM textarea from the previous
460
+ // paint (same composer, content patched in place) so the picker anchors
461
+ // near the input instead of the viewport origin.
462
+ const anchorEl = taEl || (typeof document !== 'undefined' ? document.querySelector('.chat-composer textarea') : null);
463
+ const insertEmoji = (ch) => {
464
+ const v = (taEl && taEl.value) || value || '';
465
+ const m = EMOJI_TRIGGER_RE.exec(v);
466
+ const next = m ? (v.slice(0, m.index) + (m[0].startsWith(':') ? '' : v[m.index]) + ch + ' ') : (v + ch);
467
+ if (onInput) onInput(next);
468
+ if (taEl) {
469
+ taEl.value = next;
470
+ taEl.focus();
471
+ taEl.selectionStart = taEl.selectionEnd = next.length;
472
+ }
473
+ };
450
474
  let autoGrowScheduled = false;
451
475
  const autoGrow = (e) => {
452
476
  const ta = e.target;
@@ -517,6 +541,14 @@ export function ChatComposer({ value, onInput, onSend, onAttach, onEmoji, onMenu
517
541
  }, joined);
518
542
  }
519
543
  const hasDraft = !!(value && value.trim());
544
+ const triggerPicker = triggerMatch ? EmojiPicker({
545
+ open: true,
546
+ anchorX: (anchorEl && anchorEl.getBoundingClientRect) ? anchorEl.getBoundingClientRect().left : 0,
547
+ anchorY: (anchorEl && anchorEl.getBoundingClientRect) ? anchorEl.getBoundingClientRect().top : 0,
548
+ query: triggerMatch[2] || '',
549
+ onSelect: (ch) => insertEmoji(ch),
550
+ 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(); } },
551
+ }) : null;
520
552
  return h('div', {
521
553
  class: 'chat-composer' + (hasDraft ? ' has-draft' : '') + (disabled ? ' is-disabled' : ''),
522
554
  // A drop on the composer must NEVER navigate the browser away from the
@@ -535,6 +567,7 @@ export function ChatComposer({ value, onInput, onSend, onAttach, onEmoji, onMenu
535
567
  },
536
568
  },
537
569
  contextLine,
570
+ triggerPicker,
538
571
  h('textarea', { ref: taRef, placeholder, rows: 1,
539
572
  'aria-label': label || (disabled && disabledReason ? 'message input — ' + disabledReason : 'message input'),
540
573
  disabled: !!disabled, 'aria-disabled': disabled ? 'true' : null,
@@ -375,14 +375,38 @@ export function CommandPalette({ open, items = [], onSelect, onClose } = {}) {
375
375
  // per-emoji <button> labels below. This is intentional product content, not
376
376
  // decorative chrome.
377
377
  const EMOJI_CATEGORIES = [
378
- { id: 'smileys', label: '😀', emoji: ['😀','😁','😂','🤣','😊','😍','😘','😎','🤔','😅','😉','🙂','😇','🥳','😴','🤩','😜','😢','😭','😡','😱','🥺','😤','😬'] },
379
- { id: 'gestures', label: '👍', emoji: ['👍','👎','👌','✌️','🤞','🙏','👏','🙌','💪','👀','🤝','✋','🤙','👋','🤟','☝️'] },
380
- { id: 'hearts', label: '❤️', emoji: ['❤️','🧡','💛','💚','💙','💜','🖤','🤍','💔','💕','💖','💗'] },
381
- { id: 'symbols', label: '', emoji: ['🔥','💯','','','','🎉','🎊','✨','💡','⚡','💢','💀','🚀','🏆'] },
378
+ { id: 'smileys', label: '😀', emoji: [
379
+ ['😀', 'grinning smile'], ['😁', 'grinning smile happy'], ['😂', 'joy tears laugh'], ['🤣', 'rofl laugh'],
380
+ ['😊', 'smile blush happy'], ['😍', 'heart eyes love'], ['😘', 'kiss'], ['😎', 'cool sunglasses'],
381
+ ['🤔', 'thinking'], ['😅', 'sweat smile'], ['😉', 'wink'], ['🙂', 'smile slight'],
382
+ ['😇', 'angel innocent'], ['🥳', 'party'], ['😴', 'sleep'], ['🤩', 'starstruck'],
383
+ ['😜', 'wink tongue'], ['😢', 'cry sad'], ['😭', 'sob cry'], ['😡', 'angry mad'],
384
+ ['😱', 'scream shock'], ['🥺', 'pleading'], ['😤', 'huff'], ['😬', 'grimace'],
385
+ ] },
386
+ { id: 'gestures', label: '👍', emoji: [
387
+ ['👍', 'thumbsup yes good'], ['👎', 'thumbsdown no bad'], ['👌', 'ok'], ['✌️', 'peace'],
388
+ ['🤞', 'fingers crossed'], ['🙏', 'pray thanks'], ['👏', 'clap'], ['🙌', 'raised hands'],
389
+ ['💪', 'muscle strong'], ['👀', 'eyes look'], ['🤝', 'handshake'], ['✋', 'hand stop'],
390
+ ['🤙', 'call'], ['👋', 'wave hi bye'], ['🤟', 'love you'], ['☝️', 'point up'],
391
+ ] },
392
+ { id: 'hearts', label: '❤️', emoji: [
393
+ ['❤️', 'heart love red'], ['🧡', 'heart orange'], ['💛', 'heart yellow'], ['💚', 'heart green'],
394
+ ['💙', 'heart blue'], ['💜', 'heart purple'], ['🖤', 'heart black'], ['🤍', 'heart white'],
395
+ ['💔', 'broken heart'], ['💕', 'hearts'], ['💖', 'sparkling heart'], ['💗', 'growing heart'],
396
+ ] },
397
+ { id: 'symbols', label: '✅', emoji: [
398
+ ['🔥', 'fire lit'], ['💯', 'hundred'], ['✅', 'check yes done'], ['❌', 'cross no'],
399
+ ['⭐', 'star'], ['🎉', 'party tada'], ['🎊', 'confetti'], ['✨', 'sparkles'],
400
+ ['💡', 'idea lightbulb'], ['⚡', 'zap lightning'], ['💢', 'anger'], ['💀', 'skull dead'],
401
+ ['🚀', 'rocket launch'], ['🏆', 'trophy win'],
402
+ ] },
382
403
  ];
404
+ const ALL_EMOJI = EMOJI_CATEGORIES.flatMap((c) => c.emoji);
383
405
 
384
406
  // EmojiPicker — fixed popover near (anchorX, anchorY) with category tabs + grid.
385
- export function EmojiPicker({ open, anchorX = 0, anchorY = 0, onSelect, onClose } = {}) {
407
+ // `query`, when non-empty, filters across all categories by name/keyword
408
+ // substring match (case-insensitive) instead of showing the active tab.
409
+ export function EmojiPicker({ open, anchorX = 0, anchorY = 0, onSelect, onClose, query = '' } = {}) {
386
410
  if (!open) return null;
387
411
  let cat = EMOJI_CATEGORIES[0].id;
388
412
  let rootEl = null, gridEl = null;
@@ -390,12 +414,15 @@ export function EmojiPicker({ open, anchorX = 0, anchorY = 0, onSelect, onClose
390
414
 
391
415
  const renderGrid = () => {
392
416
  if (!gridEl) return;
393
- const c = EMOJI_CATEGORIES.find(x => x.id === cat) || EMOJI_CATEGORIES[0];
417
+ const q = (query || '').trim().toLowerCase();
418
+ const cells = q
419
+ ? ALL_EMOJI.filter(([, name]) => name.toLowerCase().includes(q))
420
+ : (EMOJI_CATEGORIES.find(x => x.id === cat) || EMOJI_CATEGORIES[0]).emoji;
394
421
  webjsx.applyDiff(gridEl, h('div', { class: 'ov-emoji-grid-inner' },
395
- ...c.emoji.map((ch) => h('button', {
396
- type: 'button', class: 'ov-emoji-cell', 'aria-label': ch,
422
+ cells.length ? cells.map(([ch, name]) => h('button', {
423
+ type: 'button', class: 'ov-emoji-cell', 'aria-label': name || ch, title: name || ch,
397
424
  onclick: () => { if (onSelect) onSelect(ch); },
398
- }, ch))));
425
+ }, ch)) : h('div', { class: 'ov-emoji-empty' }, 'no emoji found')));
399
426
  };
400
427
 
401
428
  const tabNavKey = (e) => {
@@ -420,7 +447,7 @@ export function EmojiPicker({ open, anchorX = 0, anchorY = 0, onSelect, onClose
420
447
  el._ovEmojiCleanup = _anchoredOverlayLifecycle(el, { anchorX, anchorY, fallbackW: 260, fallbackH: 240, close });
421
448
  },
422
449
  },
423
- h('div', { class: 'ov-emoji-tabs', role: 'tablist' },
450
+ (query || '').trim() ? null : h('div', { class: 'ov-emoji-tabs', role: 'tablist' },
424
451
  ...EMOJI_CATEGORIES.map((c) => h('button', {
425
452
  type: 'button', class: 'ov-emoji-tab', role: 'tab',
426
453
  'aria-selected': c.id === cat ? 'true' : 'false',