anentrypoint-design 0.0.310 → 0.0.312
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/app-shell.css +10 -3
- package/chat.css +9 -0
- package/dist/247420.css +19 -3
- package/dist/247420.js +14 -14
- package/package.json +1 -1
- package/src/components/agent-chat.js +2 -1
- package/src/components/chat.js +1 -3
- package/src/components/content.js +16 -5
- package/src/components/files.js +19 -7
- package/src/components/sessions.js +14 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anentrypoint-design",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.312",
|
|
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",
|
|
@@ -161,7 +161,7 @@ export function AgentChat(props = {}) {
|
|
|
161
161
|
avatar, composerContext,
|
|
162
162
|
followups = [], onFollowupClick,
|
|
163
163
|
installHint, exportActions = [],
|
|
164
|
-
onPasteFiles, onDropFiles,
|
|
164
|
+
onPasteFiles, onDropFiles, onEmoji,
|
|
165
165
|
shownMessages, onShowEarlier,
|
|
166
166
|
} = props;
|
|
167
167
|
|
|
@@ -335,6 +335,7 @@ export function AgentChat(props = {}) {
|
|
|
335
335
|
// navigates away from a live session.
|
|
336
336
|
onPasteFiles,
|
|
337
337
|
onDropFiles,
|
|
338
|
+
onEmoji,
|
|
338
339
|
});
|
|
339
340
|
|
|
340
341
|
// Contextual follow-up chips below the last SETTLED assistant turn (claude.ai/
|
package/src/components/chat.js
CHANGED
|
@@ -477,7 +477,7 @@ function flashComposerNote(composerEl, text) {
|
|
|
477
477
|
note._dsNoteTimer = setTimeout(() => { note.remove(); }, 2600);
|
|
478
478
|
}
|
|
479
479
|
|
|
480
|
-
export function ChatComposer({ value, onInput, onSend,
|
|
480
|
+
export function ChatComposer({ value, onInput, onSend, onEmoji, onCancel, busy, placeholder = 'message…', disabled, disabledReason, label, context, onPasteFiles, onDropFiles }) {
|
|
481
481
|
// Keep a handle to the live textarea so send() reads the actual DOM value
|
|
482
482
|
// (not the possibly-lagging `value` prop) and so we can sync the DOM value
|
|
483
483
|
// only when it genuinely differs — re-applying `value` on every parent
|
|
@@ -644,9 +644,7 @@ export function ChatComposer({ value, onInput, onSend, onAttach, onEmoji, onMenu
|
|
|
644
644
|
// (CSS) to save rows. Middot is kept product typography.
|
|
645
645
|
h('div', { class: 'chat-composer-hint', 'aria-hidden': 'true' }, 'Enter to send · Shift+Enter for a new line'),
|
|
646
646
|
h('div', { class: 'chat-composer-toolbar' },
|
|
647
|
-
onAttach ? h('button', { type: 'button', class: 'composer-btn', onclick: (e) => { e.preventDefault(); onAttach(e); }, 'aria-label': 'attach file', title: 'attach file' }, Icon('paperclip')) : null,
|
|
648
647
|
onEmoji ? h('button', { type: 'button', class: 'composer-btn', onclick: (e) => { e.preventDefault(); onEmoji(e); }, 'aria-label': 'emoji picker', title: 'emoji picker (Ctrl+;)' }, Icon('smile')) : null,
|
|
649
|
-
onMenu ? h('button', { type: 'button', class: 'composer-btn', onclick: (e) => { e.preventDefault(); onMenu(e); }, 'aria-label': 'composer menu', title: 'more options' }, Icon('more-horizontal')) : null,
|
|
650
648
|
busy && onCancel
|
|
651
649
|
? h('button', { type: 'button', class: 'send cancel', onclick: (e) => { e.preventDefault(); onCancel(e); }, 'aria-label': 'stop generating', title: 'stop generating (Esc)' }, Icon('square'))
|
|
652
650
|
: h('button', { type: 'button', class: 'send', disabled: disabled || !(value && value.trim()), onclick: send,
|
|
@@ -521,9 +521,9 @@ export function PageHeader({ title, lede, eyebrow, right, compact, dense, id })
|
|
|
521
521
|
);
|
|
522
522
|
}
|
|
523
523
|
|
|
524
|
-
export function SearchInput({ value = '', placeholder = 'search…', onInput, onSubmit, name = 'q', key, label }) {
|
|
525
|
-
|
|
526
|
-
key,
|
|
524
|
+
export function SearchInput({ value = '', placeholder = 'search…', onInput, onSubmit, name = 'q', key, label, resultCount }) {
|
|
525
|
+
const input = h('input', {
|
|
526
|
+
key: 'i',
|
|
527
527
|
type: 'search',
|
|
528
528
|
name,
|
|
529
529
|
class: 'ds-search-input',
|
|
@@ -531,9 +531,20 @@ export function SearchInput({ value = '', placeholder = 'search…', onInput, on
|
|
|
531
531
|
'aria-label': label || placeholder,
|
|
532
532
|
value,
|
|
533
533
|
oninput: onInput ? (e) => onInput(e.target.value, e) : null,
|
|
534
|
-
|
|
535
|
-
|
|
534
|
+
onkeydown: (e) => {
|
|
535
|
+
// Escape clears the field in place (stays focused) rather than
|
|
536
|
+
// falling through to whatever ancestor Escape handler exists.
|
|
537
|
+
if (e.key === 'Escape' && value) { e.preventDefault(); e.stopPropagation(); if (onInput) onInput('', e); return; }
|
|
538
|
+
// IME guard: the Enter that commits a CJK composition must not submit.
|
|
539
|
+
if (onSubmit && e.key === 'Enter' && !e.isComposing && e.keyCode !== 229) onSubmit(e.target.value, e);
|
|
540
|
+
}
|
|
536
541
|
});
|
|
542
|
+
if (resultCount == null) return input;
|
|
543
|
+
// Visually-hidden live region announces the result count as it changes,
|
|
544
|
+
// without changing SearchInput's return shape for callers that don't pass it.
|
|
545
|
+
return h('span', { key, class: 'ds-search-input-wrap' },
|
|
546
|
+
input,
|
|
547
|
+
h('span', { key: 'cnt', class: 'sr-only', role: 'status', 'aria-live': 'polite' }, resultCount));
|
|
537
548
|
}
|
|
538
549
|
|
|
539
550
|
export function TextField({ label, value = '', type = 'text', placeholder = '', onInput, onChange, name, key, hint, multiline, rows = 4, maxLength, min, max, error, title, 'aria-label': ariaLabel, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy }) {
|
package/src/components/files.js
CHANGED
|
@@ -277,13 +277,25 @@ export function FileGrid({ files = [], onOpen, onAction, onUp, emptyText = 'No f
|
|
|
277
277
|
// One toolbar baseline: filter + select-all + sort sit left, density is
|
|
278
278
|
// pushed right by the spread. The filter used to be a separate right-aligned
|
|
279
279
|
// strip ABOVE controls, giving two strips with conflicting alignment.
|
|
280
|
-
const filterCtl = filter ? h('
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
280
|
+
const filterCtl = filter ? h('span', { key: 'filterwrap', class: 'ds-file-filter-wrap' },
|
|
281
|
+
h('input', {
|
|
282
|
+
key: 'filter',
|
|
283
|
+
class: 'ds-file-filter-input', type: 'search',
|
|
284
|
+
value: filter.value || '', placeholder: filter.placeholder || 'Filter files',
|
|
285
|
+
'aria-label': filter.placeholder || 'Filter files in this directory',
|
|
286
|
+
oninput: (e) => filter.onInput && filter.onInput(e.target.value),
|
|
287
|
+
onkeydown: (e) => {
|
|
288
|
+
if (e.key === 'Escape' && filter.value) { e.preventDefault(); e.stopPropagation(); filter.onInput && filter.onInput(''); }
|
|
289
|
+
},
|
|
290
|
+
}),
|
|
291
|
+
// Announces the filtered count as the filter narrows the list, so a
|
|
292
|
+
// screen-reader user gets the same feedback a sighted user reads off
|
|
293
|
+
// the grid without having to re-scan it after every keystroke. `files`
|
|
294
|
+
// here is already the host's filter-applied set (see hasFilter above) -
|
|
295
|
+
// there is no separate pre-filter total available inside this component.
|
|
296
|
+
h('span', { key: 'filtercount', class: 'sr-only', role: 'status', 'aria-live': 'polite' },
|
|
297
|
+
hasFilter ? files.length + (files.length === 1 ? ' file' : ' files') + ' shown' : '')
|
|
298
|
+
) : null;
|
|
287
299
|
const leftKids = [filterCtl, selectAllCtl, head].filter(Boolean);
|
|
288
300
|
const controlsKids = [
|
|
289
301
|
...leftKids,
|
|
@@ -35,7 +35,12 @@ export function fmtDuration(ms) {
|
|
|
35
35
|
export function ConversationList({ sessions = [], selected, groups, search, caption,
|
|
36
36
|
onSelect, onNew, newLabel = 'New chat',
|
|
37
37
|
emptyText = 'No conversations yet', loading = false, error = null,
|
|
38
|
-
loadingText = 'Loading conversations…'
|
|
38
|
+
loadingText = 'Loading conversations…',
|
|
39
|
+
// hasMore/onLoadMore: the rail's host truncates the underlying
|
|
40
|
+
// session list at some limit (a 200+ conversation user would
|
|
41
|
+
// otherwise never reach older sessions) - mirrors the History
|
|
42
|
+
// tab's existing "load N older" EventList pattern.
|
|
43
|
+
hasMore = false, onLoadMore, loadMoreLabel = 'load more conversations' } = {}) {
|
|
39
44
|
const rowFor = (s, i) => h('div', {
|
|
40
45
|
// Stable key: prefer sid, else position - a missing/duplicate sid would make
|
|
41
46
|
// key undefined and crash webjsx applyDiff ("reading 'key'" of undefined).
|
|
@@ -97,7 +102,14 @@ export function ConversationList({ sessions = [], selected, groups, search, capt
|
|
|
97
102
|
} else {
|
|
98
103
|
inner = sessions.map(rowFor);
|
|
99
104
|
}
|
|
100
|
-
|
|
105
|
+
// The load-more row sits INSIDE the scrollable list body (not the outer
|
|
106
|
+
// .ds-sessions shell) so it scrolls with the rows it extends, matching
|
|
107
|
+
// where a user's eye already is after scrolling to the bottom of the rail.
|
|
108
|
+
const loadMoreRow = (hasMore && onLoadMore && sessions.length)
|
|
109
|
+
? h('button', { key: 'loadmore', type: 'button', class: 'ds-session-loadmore', onclick: onLoadMore }, loadMoreLabel)
|
|
110
|
+
: null;
|
|
111
|
+
const body = h('div', { key: 'body', class: 'ds-session-list', role: 'listbox', 'aria-label': caption || 'Conversations' },
|
|
112
|
+
...inner, loadMoreRow);
|
|
101
113
|
|
|
102
114
|
return h('div', { class: 'ds-sessions' },
|
|
103
115
|
h('div', { key: 'head', class: 'ds-session-head' },
|