anentrypoint-design 0.0.325 → 0.0.327

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.325",
3
+ "version": "0.0.327",
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",
@@ -81,7 +81,10 @@
81
81
  "build": "node scripts/build.mjs",
82
82
  "lint:null-children": "node scripts/lint-null-children.mjs",
83
83
  "lint:classes": "node scripts/lint-classes.mjs",
84
- "tokens": "node scripts/generate-tokens-json.mjs"
84
+ "lint:ui-kits": "node scripts/generate-ui-kit-scaffolds.mjs --check",
85
+ "tokens": "node scripts/generate-tokens-json.mjs",
86
+ "generate:ui-kits": "node scripts/generate-ui-kit-scaffolds.mjs",
87
+ "generate:preview-index": "node scripts/generate-preview-index.mjs"
85
88
  },
86
89
  "repository": {
87
90
  "type": "git",
@@ -9,6 +9,7 @@ import { makePage, api, loadingState, errorState, emptyState } from './freddie/r
9
9
  import { getRecentPaths, saveRecentPath, skillLabel, renderChatMessages } from './freddie/helpers.js';
10
10
  import { Panel, Row, Table, Kpi, PageHeader, SearchInput, TextField, Select } from './content.js';
11
11
  import { Chip, Btn, Icon } from './shell.js';
12
+ import { formatTime } from '../locale.js';
12
13
  import { ChatMessage, ChatComposer } from './chat.js';
13
14
  import { fmtTime, fmtAgo } from './sessions.js';
14
15
 
@@ -109,14 +110,14 @@ export const chat = makePage((ctx) => {
109
110
  async function send(text) {
110
111
  const t = (text || ctx.state.draft || '').trim();
111
112
  if (!t || ctx.state.sending) return;
112
- ctx.state.messages.push({ role: 'user', text: t, time: new Date().toLocaleTimeString() });
113
+ ctx.state.messages.push({ role: 'user', text: t, time: formatTime(Date.now()) });
113
114
  ctx.set({ draft: '', sending: true });
114
115
  try {
115
116
  const r = await api('/api/chat', { method: 'POST', body: { prompt: t } });
116
117
  const reply = r.result || r.content || r.message || (r.messages && r.messages.at(-1)?.content) || JSON.stringify(r);
117
- ctx.state.messages.push({ role: 'assistant', text: String(reply), time: new Date().toLocaleTimeString() });
118
+ ctx.state.messages.push({ role: 'assistant', text: String(reply), time: formatTime(Date.now()) });
118
119
  } catch (e) {
119
- ctx.state.messages.push({ role: 'assistant', text: 'Error: ' + String(e.message || e), time: new Date().toLocaleTimeString() });
120
+ ctx.state.messages.push({ role: 'assistant', text: 'Error: ' + String(e.message || e), time: formatTime(Date.now()) });
120
121
  }
121
122
  ctx.set({ sending: false });
122
123
  }
@@ -6,6 +6,7 @@
6
6
  import * as webjsx from '../../vendor/webjsx/index.js';
7
7
  import { Btn, Icon } from './shell.js';
8
8
  import { Select, SearchInput } from './content.js';
9
+ import { formatDateTime, formatNumber } from '../locale.js';
9
10
  const h = webjsx.createElement;
10
11
 
11
12
  // ONE duration format for every surface (live cards, running panel, session
@@ -15,7 +16,7 @@ const h = webjsx.createElement;
15
16
  // timestamp (freddie pages, chat transcripts). fmtTime -> localized
16
17
  // date+time string; fmtAgo -> coarse relative ('Ns/Nm/Nh/Nd ago').
17
18
  export function fmtTime(t) {
18
- try { return new Date(t).toLocaleString(); } catch { return String(t || ''); }
19
+ try { return formatDateTime(t); } catch { return String(t || ''); }
19
20
  }
20
21
  export function fmtAgo(t) {
21
22
  if (!t) return '';
@@ -231,7 +232,7 @@ export function SessionCard({ session = {}, onStop, onOpen, onView, active = fal
231
232
  const elapsedText = s.elapsedMs != null ? fmtDuration(s.elapsedMs) : (s.elapsed != null ? s.elapsed : null);
232
233
  // At-a-glance cost/usage (the prompt's named command-center signal). Null-safe:
233
234
  // sessions with no cost source (external tally rows) simply omit the segment.
234
- const tokText = s.tokens != null ? (typeof s.tokens === 'number' ? s.tokens.toLocaleString() : s.tokens) + ' tok' : null;
235
+ const tokText = s.tokens != null ? (typeof s.tokens === 'number' ? formatNumber(s.tokens) : s.tokens) + ' tok' : null;
235
236
  const costText = s.cost != null ? (typeof s.cost === 'number' ? '$' + s.cost.toFixed(4) : String(s.cost)) : null;
236
237
  // Cost is rendered as its own emphasized segment (not buried in the mono run)
237
238
  // so the command-center cost-at-a-glance signal is scannable.
@@ -3,6 +3,20 @@
3
3
  Comprehensive improvements for perfect UX across all surfaces
4
4
  ============================================================ */
5
5
 
6
+ /* ------------------------------------------------------------
7
+ Table — clickable row focus ring
8
+ Table()'s onRowClick path (content.js) sets class="clickable"
9
+ role="button" tabindex="0" on the <tr>; the browser's native
10
+ :focus-visible outline on a <tr> renders inconsistently across
11
+ engines (often clipped by table border-collapse), so it needs
12
+ the same explicit outline treatment every other interactive
13
+ primitive in this file gets, via the shared --focus-* tokens.
14
+ -------------------------------------------------------------- */
15
+ tr.clickable:focus-visible {
16
+ outline: var(--focus-w) solid var(--focus-color);
17
+ outline-offset: calc(-1 * var(--focus-offset));
18
+ }
19
+
6
20
  /* ------------------------------------------------------------
7
21
  Component States — Disabled, Loading, Error, Success
8
22
  -------------------------------------------------------------- */
package/src/locale.js ADDED
@@ -0,0 +1,84 @@
1
+ // 247420 design system — locale controller.
2
+ //
3
+ // User-configurable locale override for date/time formatting; persists to
4
+ // localStorage like theme.js's controller. Absent an override, falls back to
5
+ // navigator.language (the browser's own locale) explicitly rather than
6
+ // relying on Intl's implicit-undefined-locale default, so a caller can always
7
+ // know which locale actually formatted a given string.
8
+
9
+ const KEY = '247420:locale';
10
+
11
+ function isBrowser() {
12
+ return typeof navigator !== 'undefined' && typeof window !== 'undefined';
13
+ }
14
+
15
+ function readStored() {
16
+ try { return window.localStorage.getItem(KEY) || null; } catch { return null; }
17
+ }
18
+
19
+ export function getLocale() {
20
+ const stored = readStored();
21
+ if (stored) return stored;
22
+ if (isBrowser() && navigator.language) return navigator.language;
23
+ return 'en-US';
24
+ }
25
+
26
+ export function setLocale(locale) {
27
+ if (!isBrowser()) return locale;
28
+ try {
29
+ if (locale) window.localStorage.setItem(KEY, locale);
30
+ else window.localStorage.removeItem(KEY);
31
+ } catch { /* swallow: persistence is best-effort, locale still applies in-memory this session */ }
32
+ return locale;
33
+ }
34
+
35
+ function hour12Preference(locale) {
36
+ try { return Intl.DateTimeFormat(locale).resolvedOptions().hour12; }
37
+ catch { return undefined; }
38
+ }
39
+
40
+ // formatTime: replaces bare `new Date().toLocaleTimeString()` -- explicit
41
+ // locale (never the implicit-undefined default) plus the locale's own
42
+ // resolved hour12 preference (never hardcoded 12/24h).
43
+ export function formatTime(date, locale = getLocale()) {
44
+ const hour12 = hour12Preference(locale);
45
+ try { return new Date(date).toLocaleTimeString(locale, hour12 === undefined ? undefined : { hour12 }); }
46
+ catch { return new Date(date).toLocaleTimeString(); }
47
+ }
48
+
49
+ export function formatDateTime(date, locale = getLocale()) {
50
+ const hour12 = hour12Preference(locale);
51
+ try { return new Date(date).toLocaleString(locale, hour12 === undefined ? undefined : { hour12 }); }
52
+ catch { return new Date(date).toLocaleString(); }
53
+ }
54
+
55
+ // formatNumber: replaces bare `n.toLocaleString()` -- explicit locale for
56
+ // thousands-separator/decimal conventions that genuinely differ (e.g.
57
+ // '1,234.5' en-US vs '1.234,5' de-DE).
58
+ export function formatNumber(n, locale = getLocale()) {
59
+ try { return n.toLocaleString(locale); } catch { return String(n); }
60
+ }
61
+
62
+ const RTF_DIVISIONS = [
63
+ { amount: 60, unit: 'seconds' },
64
+ { amount: 60, unit: 'minutes' },
65
+ { amount: 24, unit: 'hours' },
66
+ { amount: 7, unit: 'days' },
67
+ { amount: 4.34524, unit: 'weeks' },
68
+ { amount: 12, unit: 'months' },
69
+ { amount: Infinity, unit: 'years' },
70
+ ];
71
+
72
+ // formatRelativeTime: "2 hours ago" via the real Intl.RelativeTimeFormat,
73
+ // never a hand-rolled string-concat implementation.
74
+ export function formatRelativeTime(date, locale = getLocale(), now = Date.now()) {
75
+ let duration = (new Date(date).getTime() - now) / 1000;
76
+ try {
77
+ const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
78
+ for (const division of RTF_DIVISIONS) {
79
+ if (Math.abs(duration) < division.amount) return rtf.format(Math.round(duration), division.unit);
80
+ duration /= division.amount;
81
+ }
82
+ } catch { /* fall through to the plain-time fallback below */ }
83
+ return formatTime(date, locale);
84
+ }