anentrypoint-design 0.0.124 → 0.0.127

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.124",
3
+ "version": "0.0.127",
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",
@@ -50,6 +50,7 @@
50
50
  "vendor/webjsx",
51
51
  "colors_and_type.css",
52
52
  "app-shell.css",
53
+ "chat.css",
53
54
  "community.css",
54
55
  "README.md"
55
56
  ],
@@ -111,7 +111,10 @@ function renderPart(p, key) {
111
111
  export function ChatMessage({ who = 'them', avatar, text, parts, time, typing, key, aicat, reactions, receipt, name }) {
112
112
  _stats.messages += 1;
113
113
  const cls = 'chat-msg ' + who + (aicat && who === 'them' ? ' aicat' : '');
114
- const av = h('span', { class: 'chat-avatar' }, avatar || (who === 'you' ? 'u' : '?'));
114
+ const fallbackAvatar = avatar != null
115
+ ? avatar
116
+ : (who === 'you' ? 'u' : (name ? String(name).trim().charAt(0).toUpperCase() || '◔' : '◔'));
117
+ const av = h('span', { class: 'chat-avatar' }, fallbackAvatar);
115
118
  let bodyNodes;
116
119
  if (typing) bodyNodes = [h('div', { class: 'chat-bubble', key: 'typb' }, h('span', { class: 'chat-typing' }, h('span'), h('span'), h('span')))];
117
120
  else if (parts && parts.length) bodyNodes = parts.map((p, i) => renderPart(p, i));
@@ -139,15 +142,36 @@ export function ChatComposer({ value, onInput, onSend, placeholder = 'message…
139
142
  if (!v || disabled) return;
140
143
  if (onSend) onSend(v);
141
144
  };
145
+ const autoGrow = (e) => {
146
+ const ta = e.target;
147
+ ta.style.height = 'auto';
148
+ ta.style.height = Math.min(ta.scrollHeight, 200) + 'px';
149
+ if (onInput) onInput(ta.value);
150
+ };
151
+ const taRef = (el) => {
152
+ if (!el) return;
153
+ // initial sizing reflects current value
154
+ el.style.height = 'auto';
155
+ el.style.height = Math.min(el.scrollHeight, 200) + 'px';
156
+ };
142
157
  return h('div', { class: 'chat-composer' },
143
- h('textarea', { value: value || '', placeholder, rows: 1,
144
- oninput: (e) => onInput && onInput(e.target.value),
158
+ h('textarea', { ref: taRef, value: value || '', placeholder, rows: 1,
159
+ oninput: autoGrow,
145
160
  onkeydown: (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); } } }),
146
161
  h('button', { class: 'send', disabled: disabled || !(value && value.trim()), onclick: send }, '↑')
147
162
  );
148
163
  }
149
164
 
150
165
  export function Chat({ title = 'chat', sub, messages = [], composer, header } = {}) {
166
+ const threadRef = (el) => {
167
+ if (!el) return;
168
+ const target = el.scrollHeight - el.clientHeight;
169
+ // only auto-scroll if user is near the bottom (within 120px) — preserves manual scroll-back
170
+ if (target - el.scrollTop < 240 || el.dataset.msgCount !== String(messages.length)) {
171
+ el.scrollTop = target;
172
+ el.dataset.msgCount = String(messages.length);
173
+ }
174
+ };
151
175
  return h('div', { class: 'chat' },
152
176
  header || h('div', { class: 'chat-head' },
153
177
  h('span', { class: 'dot' }),
@@ -156,7 +180,7 @@ export function Chat({ title = 'chat', sub, messages = [], composer, header } =
156
180
  h('span', { class: 'spread' }),
157
181
  h('span', { class: 'sub' }, String(messages.length).padStart(2, '0') + ' msgs')
158
182
  ),
159
- h('div', { class: 'chat-thread' },
183
+ h('div', { class: 'chat-thread', ref: threadRef },
160
184
  ...messages.map((m, i) => ChatMessage({ ...m, key: m.key != null ? m.key : i }))
161
185
  ),
162
186
  composer || null
@@ -181,6 +205,14 @@ export function AICat({ name = 'aicat', messages = [], thinking, composer, statu
181
205
  const all = thinking
182
206
  ? [...annotated, { who: 'them', aicat: true, avatar: '=^.^=', typing: true, key: '_thinking' }]
183
207
  : annotated;
208
+ const threadRef = (el) => {
209
+ if (!el) return;
210
+ const target = el.scrollHeight - el.clientHeight;
211
+ if (target - el.scrollTop < 240 || el.dataset.msgCount !== String(all.length)) {
212
+ el.scrollTop = target;
213
+ el.dataset.msgCount = String(all.length);
214
+ }
215
+ };
184
216
  return h('div', { class: 'chat' },
185
217
  h('div', { class: 'chat-head' },
186
218
  h('span', { class: 'dot' }),
@@ -189,7 +221,7 @@ export function AICat({ name = 'aicat', messages = [], thinking, composer, statu
189
221
  h('span', { class: 'spread' }),
190
222
  h('span', { class: 'sub' }, String(messages.length).padStart(2, '0') + ' turns')
191
223
  ),
192
- h('div', { class: 'chat-thread' },
224
+ h('div', { class: 'chat-thread', ref: threadRef },
193
225
  ...all.map((m, i) => ChatMessage({ ...m, key: m.key != null ? m.key : i }))
194
226
  ),
195
227
  composer || null
@@ -245,12 +245,33 @@ export function TextField({ label, value = '', type = 'text', placeholder = '',
245
245
  );
246
246
  }
247
247
 
248
- export function EventList({ items = [], emptyText = 'no events', rankPad = 3 }) {
249
- if (!items || !items.length) return h('p', { class: 'lede' }, emptyText);
248
+ export function Select({ label, value = '', options = [], onChange, name, key, placeholder, hint }) {
249
+ const opts = [];
250
+ if (placeholder != null) opts.push(h('option', { key: '_ph', value: '' }, placeholder));
251
+ for (const o of options) {
252
+ const id = typeof o === 'string' ? o : (o.value != null ? o.value : o.id);
253
+ const lab = typeof o === 'string' ? o : (o.label != null ? o.label : (o.id || o.value));
254
+ opts.push(h('option', { key: 'o-' + id, value: id, selected: id === value }, lab));
255
+ }
256
+ const select = h('select', {
257
+ key: 'i', name, class: 'ds-select',
258
+ onchange: onChange ? (e) => onChange(e.target.value, e) : null
259
+ }, ...opts);
260
+ if (label == null && hint == null) return select;
261
+ return h('label', { key, class: 'ds-field' },
262
+ label != null ? h('span', { key: 'l', class: 'ds-field-label' }, label) : null,
263
+ select,
264
+ hint != null ? h('span', { key: 'h', class: 'lede ds-field-hint' }, hint) : null
265
+ );
266
+ }
267
+
268
+ export function EventList({ items, events, emptyText = 'no events', rankPad = 3 }) {
269
+ const list = items || events || [];
270
+ if (!list.length) return h('p', { class: 'lede' }, emptyText);
250
271
  return h('section', { class: 'ds-section ds-event-list' },
251
- ...items.map((it, i) => Row({
272
+ ...list.map((it, i) => Row({
252
273
  key: it.key || ('ev' + i),
253
- code: String(i + 1).padStart(rankPad, '0'),
274
+ code: it.code != null ? it.code : (it.rank != null ? it.rank : String(i + 1).padStart(rankPad, '0')),
254
275
  title: it.title || '(empty)',
255
276
  sub: it.sub || '',
256
277
  active: it.active,
package/src/components.js CHANGED
@@ -13,7 +13,7 @@ export {
13
13
  Panel, Row, RowLink,
14
14
  Hero, Install, Receipt, Changelog,
15
15
  WorksList, WritingList, Manifesto, Section, PageHeader,
16
- Kpi, Table, SearchInput, TextField, EventList,
16
+ Kpi, Table, SearchInput, TextField, Select, EventList,
17
17
  HomeView, ProjectView, Form
18
18
  } from './components/content.js';
19
19