@jini-ai/chat 0.6.5 → 0.6.7

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.
Files changed (161) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/core/accepted-input.d.ts +54 -0
  3. package/dist/core/accepted-input.js +165 -0
  4. package/dist/core/agent-plugin-chat-state.d.ts +43 -0
  5. package/dist/core/agent-plugin-chat-state.js +48 -0
  6. package/dist/core/assistant-client/a2ui-action-poster.js +32 -28
  7. package/dist/core/assistant-client/messages.d.ts +1 -1
  8. package/dist/core/assistant-client/messages.js +2 -2
  9. package/dist/core/assistant-client/transport.d.ts +2 -2
  10. package/dist/core/assistant-client/transport.js +184 -97
  11. package/dist/core/assistant-content.d.ts +2 -1
  12. package/dist/core/assistant-content.js +9 -11
  13. package/dist/core/browser/page-actions.js +4 -2
  14. package/dist/core/composer-history.d.ts +16 -9
  15. package/dist/core/composer-history.js +28 -16
  16. package/dist/core/conversation-title.d.ts +3 -0
  17. package/dist/core/conversation-title.js +3 -0
  18. package/dist/core/durable-projection.js +20 -13
  19. package/dist/core/durable-runs/continuation.d.ts +4 -0
  20. package/dist/core/durable-runs/continuation.js +35 -6
  21. package/dist/core/durable-runs/ledger-contracts.d.ts +2 -1
  22. package/dist/core/durable-runs/policies.d.ts +1 -0
  23. package/dist/core/durable-runs/policies.js +1 -0
  24. package/dist/core/durable-runs/ports.d.ts +6 -2
  25. package/dist/core/index.d.ts +2 -0
  26. package/dist/core/index.js +2 -0
  27. package/dist/core/messages.d.ts +2 -0
  28. package/dist/core/partial-json.d.ts +6 -7
  29. package/dist/core/partial-json.js +81 -47
  30. package/dist/core/persistence/title.js +4 -2
  31. package/dist/core/question-answer.d.ts +23 -0
  32. package/dist/core/question-answer.js +66 -0
  33. package/dist/core/question-form/format.d.ts +6 -6
  34. package/dist/core/question-form/format.js +16 -17
  35. package/dist/core/question-form/normalize.d.ts +18 -0
  36. package/dist/core/question-form/normalize.js +112 -122
  37. package/dist/core/question-form/scan.js +141 -203
  38. package/dist/core/run-activity.js +62 -49
  39. package/dist/core/run-events/entry.d.ts +10 -1
  40. package/dist/core/run-events/entry.js +117 -87
  41. package/dist/core/todos.d.ts +1 -1
  42. package/dist/core/todos.js +60 -85
  43. package/dist/core/tool-events.js +14 -12
  44. package/dist/core/transcript.js +62 -16
  45. package/dist/core/turn-usage.d.ts +2 -1
  46. package/dist/core/turn-usage.js +56 -27
  47. package/dist/core/user-text-redaction.js +3 -0
  48. package/dist/core/util/manifest.js +77 -82
  49. package/dist/core/util/markdown-context.js +51 -53
  50. package/dist/core/util/parser.js +136 -144
  51. package/dist/core/util/pointer.js +16 -16
  52. package/dist/core/util/strip.js +57 -67
  53. package/dist/core/util/validate.js +15 -17
  54. package/dist/embed-widget/browser/session-store.js +21 -12
  55. package/dist/embed-widget/transports/fetch-sse.js +108 -95
  56. package/dist/embed-widget/vanilla/index.js +81 -70
  57. package/dist/react/agent-bridge/frontend-session-bridge.js +80 -52
  58. package/dist/react/components/AttachmentPreviewModal.js +25 -1
  59. package/dist/react/components/ChatSettingsDialog.d.ts +13 -0
  60. package/dist/react/components/ChatSettingsDialog.hooks.d.ts +115 -0
  61. package/dist/react/components/ChatSettingsDialog.hooks.js +40 -0
  62. package/dist/react/components/ChatSettingsDialog.js +72 -0
  63. package/dist/react/components/ChatSettingsSegmented.d.ts +18 -0
  64. package/dist/react/components/ChatSettingsSegmented.js +11 -0
  65. package/dist/react/components/Composer.d.ts +5 -1
  66. package/dist/react/components/Composer.js +126 -82
  67. package/dist/react/components/ConversationList.js +25 -39
  68. package/dist/react/components/ConversationTitleEditor.d.ts +27 -0
  69. package/dist/react/components/ConversationTitleEditor.js +54 -0
  70. package/dist/react/components/Icon.d.ts +1 -1
  71. package/dist/react/components/Icon.js +25 -45
  72. package/dist/react/components/Markdown.js +134 -123
  73. package/dist/react/components/McpUiSurfaceCard.d.ts +2 -2
  74. package/dist/react/components/McpUiSurfaceCard.js +30 -9
  75. package/dist/react/components/MessageList.d.ts +13 -13
  76. package/dist/react/components/MessageList.hooks.d.ts +6 -1
  77. package/dist/react/components/MessageList.hooks.js +19 -3
  78. package/dist/react/components/MessageList.js +34 -10
  79. package/dist/react/components/MessageRow.js +48 -32
  80. package/dist/react/components/MessageRow.usage.d.ts +5 -2
  81. package/dist/react/components/MessageRow.usage.js +20 -11
  82. package/dist/react/components/MidRunUserMessage.js +16 -3
  83. package/dist/react/components/QuestionForm.js +148 -76
  84. package/dist/react/components/QuestionsPanel.js +1 -1
  85. package/dist/react/components/TodoCard.js +12 -6
  86. package/dist/react/components/ToolCard.js +47 -40
  87. package/dist/react/components/composer-discovery.js +6 -1
  88. package/dist/react/components/mcp-ui-surface-state.d.ts +6 -11
  89. package/dist/react/components/mcp-ui-surface-state.js +23 -8
  90. package/dist/react/ext-event-renderer-registry.d.ts +8 -0
  91. package/dist/react/ext-event-renderer-registry.js +5 -2
  92. package/dist/react/features/chat-pane/components/AgentPluginUsage.d.ts +7 -0
  93. package/dist/react/features/chat-pane/components/AgentPluginUsage.js +45 -0
  94. package/dist/react/features/chat-pane/components/AgentRuntimePicker.js +55 -19
  95. package/dist/react/features/chat-pane/components/ChatPane.d.ts +1 -1
  96. package/dist/react/features/chat-pane/components/ChatPane.js +192 -74
  97. package/dist/react/features/chat-pane/components/RuntimeDropUp.d.ts +29 -0
  98. package/dist/react/features/chat-pane/components/RuntimeDropUp.js +45 -0
  99. package/dist/react/features/chat-pane/create-daemon-attachment-uploader.js +80 -47
  100. package/dist/react/features/chat-pane/create-mcp-ui-tool-caller.js +44 -40
  101. package/dist/react/features/chat-pane/create-mid-run-message-poster.d.ts +6 -6
  102. package/dist/react/features/chat-pane/create-mid-run-message-poster.js +8 -8
  103. package/dist/react/features/chat-pane/create-typed-answer-poster.d.ts +3 -3
  104. package/dist/react/features/chat-pane/create-typed-answer-poster.js +3 -3
  105. package/dist/react/features/chat-pane/hooks/useAgentRuntimePicker.hooks.d.ts +6 -1
  106. package/dist/react/features/chat-pane/hooks/useAgentRuntimePicker.hooks.js +11 -5
  107. package/dist/react/features/chat-pane/hooks/useChatPane.hooks.d.ts +12 -7
  108. package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js +340 -149
  109. package/dist/react/features/chat-pane/hooks/useChatPaneControlsHeight.hooks.js +7 -6
  110. package/dist/react/features/chat-pane/hooks/useChatPaneNewTabLinks.hooks.js +28 -10
  111. package/dist/react/features/chat-pane/hooks/useChatPaneRuntimeInventory.hooks.d.ts +3 -3
  112. package/dist/react/features/chat-pane/hooks/useChatPaneRuntimeInventory.hooks.js +60 -12
  113. package/dist/react/features/chat-pane/hooks/useRuntimeDropUp.hooks.d.ts +49 -0
  114. package/dist/react/features/chat-pane/hooks/useRuntimeDropUp.hooks.js +143 -0
  115. package/dist/react/features/chat-pane/model-options.d.ts +18 -1
  116. package/dist/react/features/chat-pane/model-options.js +49 -20
  117. package/dist/react/features/chat-pane/rules.d.ts +8 -9
  118. package/dist/react/features/chat-pane/rules.js +84 -46
  119. package/dist/react/features/chat-pane/styles.d.ts +1 -1
  120. package/dist/react/features/chat-pane/styles.js +156 -37
  121. package/dist/react/features/chat-pane/types.d.ts +17 -8
  122. package/dist/react/hooks/assistant-chats.js +12 -5
  123. package/dist/react/hooks/composer-draft-cache.js +116 -118
  124. package/dist/react/hooks/composer-history-storage.js +5 -5
  125. package/dist/react/hooks/fab-position.js +7 -4
  126. package/dist/react/hooks/useAnsweredCards.d.ts +21 -4
  127. package/dist/react/hooks/useAnsweredCards.js +38 -11
  128. package/dist/react/hooks/useArtifactStream.js +28 -22
  129. package/dist/react/hooks/useComposer.d.ts +20 -0
  130. package/dist/react/hooks/useComposer.js +22 -9
  131. package/dist/react/hooks/useComposerHistory.hooks.d.ts +6 -1
  132. package/dist/react/hooks/useComposerHistory.hooks.js +39 -19
  133. package/dist/react/hooks/useComposerHistoryKeys.hooks.js +24 -14
  134. package/dist/react/hooks/useConversation.d.ts +7 -0
  135. package/dist/react/hooks/useConversation.js +23 -10
  136. package/dist/react/hooks/useExtEventGroups.js +39 -39
  137. package/dist/react/hooks/useQuestionForms.js +21 -26
  138. package/dist/react/hooks/useRunStream.js +10 -2
  139. package/dist/react/index.d.ts +7 -1
  140. package/dist/react/index.js +9 -0
  141. package/dist/react/message-blocks.d.ts +6 -4
  142. package/dist/react/message-blocks.js +94 -72
  143. package/dist/react/slots.d.ts +2 -2
  144. package/dist/react/tool-call-display.js +19 -17
  145. package/dist/server/durable-runs/recover.d.ts +1 -1
  146. package/dist/server/durable-runs/recover.js +139 -76
  147. package/dist/server/run-finalizer/entry.js +25 -15
  148. package/dist/store/legacy/sqlite/conversations.d.ts +11 -11
  149. package/dist/store/legacy/sqlite/conversations.js +69 -98
  150. package/dist/store/legacy/sqlite/messages.d.ts +10 -2
  151. package/dist/store/legacy/sqlite/messages.js +113 -71
  152. package/dist/store/runtime/chat-history-store.js +9 -3
  153. package/dist/store/runtime/durable-run-store.d.ts +2 -2
  154. package/dist/store/runtime/durable-run-store.js +165 -117
  155. package/dist/store/runtime/history-tools.js +118 -82
  156. package/dist/store/runtime/run-ledger.d.ts +4 -1
  157. package/dist/store/runtime/run-ledger.js +56 -16
  158. package/dist/store/sql/cursor.js +38 -18
  159. package/dist/store/sql/store.d.ts +18 -0
  160. package/dist/store/sql/store.js +46 -15
  161. package/package.json +12 -10
@@ -10,6 +10,23 @@
10
10
  * question objects.
11
11
  */
12
12
  import type { FormQuestion } from './types.js';
13
+ /** `raw` viewed as a record of fields, or `null` when it isn't an object at all. */
14
+ export declare function asRecord(raw: unknown): Record<string, unknown> | null;
15
+ /** `raw` when it is a string, else `undefined`. */
16
+ export declare function stringOrUndefined(raw: unknown): string | undefined;
17
+ /** `raw` when it is a non-empty string, else `undefined`. */
18
+ export declare function nonEmptyString(raw: unknown): string | undefined;
19
+ type OmitUndefined<T> = {
20
+ [K in keyof T as undefined extends T[K] ? never : K]: T[K];
21
+ } & {
22
+ [K in keyof T as undefined extends T[K] ? K : never]?: Exclude<T[K], undefined>;
23
+ };
24
+ /**
25
+ * `obj` without its `undefined`-valued keys, in the original key order — so
26
+ * an optional field is absent rather than present-but-undefined, which
27
+ * `exactOptionalPropertyTypes` (and a consumer's `'key' in obj`) can tell apart.
28
+ */
29
+ export declare function omitUndefined<T extends Record<string, unknown>>(obj: T): OmitUndefined<T>;
13
30
  export declare function mapRawQuestion({ q, index }: {
14
31
  q: unknown;
15
32
  index: number;
@@ -32,4 +49,5 @@ export declare function mapRawQuestion({ q, index }: {
32
49
  export declare function isRenderableColor({ value }: {
33
50
  value: string;
34
51
  }): boolean;
52
+ export {};
35
53
  //# sourceMappingURL=normalize.d.ts.map
@@ -1,89 +1,93 @@
1
1
  import { formOptionValueForLabel } from './format.js';
2
+ /** `raw` viewed as a record of fields, or `null` when it isn't an object at all. */
3
+ export function asRecord(raw) {
4
+ return raw && typeof raw === 'object' ? raw : null;
5
+ }
6
+ /** `raw` when it is a string, else `undefined`. */
7
+ export function stringOrUndefined(raw) {
8
+ return typeof raw === 'string' ? raw : undefined;
9
+ }
10
+ /** `raw` when it is a non-empty string, else `undefined`. */
11
+ export function nonEmptyString(raw) {
12
+ return stringOrUndefined(raw) || undefined;
13
+ }
14
+ /** `raw` trimmed when it is a string with visible content, else `undefined`. */
15
+ function trimmedNonBlank(raw) {
16
+ return nonEmptyString(stringOrUndefined(raw)?.trim());
17
+ }
18
+ /** The string entries of `raw` when it is an array, else an empty list. */
19
+ function stringsOnly(raw) {
20
+ return Array.isArray(raw) ? raw.filter((entry) => typeof entry === 'string') : [];
21
+ }
22
+ /**
23
+ * `obj` without its `undefined`-valued keys, in the original key order — so
24
+ * an optional field is absent rather than present-but-undefined, which
25
+ * `exactOptionalPropertyTypes` (and a consumer's `'key' in obj`) can tell apart.
26
+ */
27
+ export function omitUndefined(obj) {
28
+ return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));
29
+ }
2
30
  export function mapRawQuestion({ q, index }) {
3
- if (!q || typeof q !== 'object')
31
+ const qo = asRecord(q);
32
+ if (!qo)
4
33
  return null;
5
- const qo = q;
6
- const id = typeof qo.id === 'string' && qo.id.trim().length > 0 ? qo.id.trim() : `q${index + 1}`;
7
- const label = typeof qo.label === 'string' ? qo.label : id;
34
+ const id = trimmedNonBlank(qo.id) ?? `q${index + 1}`;
8
35
  const type = normalizeType(qo.type);
9
36
  const options = parseOptions(qo.options);
10
- const placeholder = typeof qo.placeholder === 'string' ? qo.placeholder : undefined;
11
- const help = typeof qo.help === 'string' ? qo.help : undefined;
12
- const required = qo.required === true;
13
- const maxSelections = typeof qo.maxSelections === 'number' && Number.isInteger(qo.maxSelections) && qo.maxSelections > 0
14
- ? qo.maxSelections
15
- : undefined;
16
- const cards = parseDirectionCards(qo.cards);
17
- const defaultValue = parseDefaultValue(qo, options);
18
- const allowCustom = qo.allowCustom === false ? false : qo.allowCustom === true || qo.custom === true ? true : undefined;
19
- const customLabel = typeof qo.customLabel === 'string' ? qo.customLabel : undefined;
20
- const customPlaceholder = typeof qo.customPlaceholder === 'string' ? qo.customPlaceholder : undefined;
21
- const min = parseNumberAttr(qo.min);
22
- const max = parseNumberAttr(qo.max);
23
- const step = parseNumberAttr(qo.step);
24
- const multiple = qo.multiple === true;
25
- const accept = typeof qo.accept === 'string' ? qo.accept : undefined;
26
- return {
37
+ const isFile = type === 'file';
38
+ return omitUndefined({
27
39
  id,
28
- ...(typeof qo.name === 'string' ? { name: qo.name } : {}),
29
- label,
40
+ name: stringOrUndefined(qo.name),
41
+ label: stringOrUndefined(qo.label) ?? id,
30
42
  type,
31
- ...(options ? { options } : {}),
32
- ...(placeholder ? { placeholder } : {}),
33
- ...(help ? { help } : {}),
34
- ...(required ? { required } : {}),
35
- ...(defaultValue !== undefined ? { defaultValue } : {}),
36
- ...(maxSelections !== undefined && type === 'checkbox' ? { maxSelections } : {}),
37
- ...(allowCustom !== undefined ? { allowCustom } : {}),
38
- ...(customLabel ? { customLabel } : {}),
39
- ...(customPlaceholder ? { customPlaceholder } : {}),
40
- ...(min !== undefined ? { min } : {}),
41
- ...(max !== undefined ? { max } : {}),
42
- ...(step !== undefined ? { step } : {}),
43
- ...(multiple && type === 'file' ? { multiple } : {}),
44
- ...(accept && type === 'file' ? { accept } : {}),
45
- ...(cards ? { cards } : {}),
46
- };
43
+ options,
44
+ placeholder: nonEmptyString(qo.placeholder),
45
+ help: nonEmptyString(qo.help),
46
+ required: qo.required === true || undefined,
47
+ defaultValue: parseDefaultValue(qo, options),
48
+ maxSelections: type === 'checkbox' ? positiveInteger(qo.maxSelections) : undefined,
49
+ allowCustom: parseAllowCustom(qo),
50
+ customLabel: nonEmptyString(qo.customLabel),
51
+ customPlaceholder: nonEmptyString(qo.customPlaceholder),
52
+ min: parseNumberAttr(qo.min),
53
+ max: parseNumberAttr(qo.max),
54
+ step: parseNumberAttr(qo.step),
55
+ multiple: (isFile && qo.multiple === true) || undefined,
56
+ accept: isFile ? nonEmptyString(qo.accept) : undefined,
57
+ cards: parseDirectionCards(qo.cards),
58
+ });
59
+ }
60
+ function positiveInteger(raw) {
61
+ return typeof raw === 'number' && Number.isInteger(raw) && raw > 0 ? raw : undefined;
47
62
  }
63
+ // An explicit `allowCustom: false` wins over the legacy `custom: true` key.
64
+ function parseAllowCustom(question) {
65
+ if (question.allowCustom === false)
66
+ return false;
67
+ return question.allowCustom === true || question.custom === true || undefined;
68
+ }
69
+ const TYPE_ALIASES = new Map([
70
+ ['radio', ['radio', 'single', 'choice']],
71
+ ['checkbox', ['checkbox', 'multi', 'multiple']],
72
+ ['select', ['select', 'dropdown']],
73
+ ['textarea', ['textarea', 'long', 'paragraph']],
74
+ ['number', ['number', 'numeric']],
75
+ ['range', ['range', 'slider']],
76
+ ['date', ['date']],
77
+ ['time', ['time']],
78
+ ['datetime-local', ['datetime-local', 'datetime', 'date-time', 'datetime_local']],
79
+ ['color', ['color', 'colour', 'color-picker']],
80
+ ['url', ['url', 'link']],
81
+ ['email', ['email']],
82
+ ['tel', ['tel', 'phone']],
83
+ ['file', ['file', 'upload', 'attachment']],
84
+ ['switch', ['switch', 'toggle', 'boolean']],
85
+ ['direction-cards', ['direction-cards', 'directions', 'cards', 'direction']],
86
+ ].flatMap(([type, aliases]) => aliases.map((alias) => [alias, type])));
48
87
  function normalizeType(raw) {
49
88
  if (typeof raw !== 'string')
50
89
  return 'text';
51
- const lower = raw.toLowerCase().trim();
52
- if (lower === 'radio' || lower === 'single' || lower === 'choice')
53
- return 'radio';
54
- if (lower === 'checkbox' || lower === 'multi' || lower === 'multiple')
55
- return 'checkbox';
56
- if (lower === 'select' || lower === 'dropdown')
57
- return 'select';
58
- if (lower === 'textarea' || lower === 'long' || lower === 'paragraph')
59
- return 'textarea';
60
- if (lower === 'number' || lower === 'numeric')
61
- return 'number';
62
- if (lower === 'range' || lower === 'slider')
63
- return 'range';
64
- if (lower === 'date')
65
- return 'date';
66
- if (lower === 'time')
67
- return 'time';
68
- if (lower === 'datetime-local' || lower === 'datetime' || lower === 'date-time' || lower === 'datetime_local') {
69
- return 'datetime-local';
70
- }
71
- if (lower === 'color' || lower === 'colour' || lower === 'color-picker')
72
- return 'color';
73
- if (lower === 'url' || lower === 'link')
74
- return 'url';
75
- if (lower === 'email')
76
- return 'email';
77
- if (lower === 'tel' || lower === 'phone')
78
- return 'tel';
79
- if (lower === 'file' || lower === 'upload' || lower === 'attachment')
80
- return 'file';
81
- if (lower === 'switch' || lower === 'toggle' || lower === 'boolean')
82
- return 'switch';
83
- if (lower === 'direction-cards' || lower === 'directions' || lower === 'cards' || lower === 'direction') {
84
- return 'direction-cards';
85
- }
86
- return 'text';
90
+ return TYPE_ALIASES.get(raw.toLowerCase().trim()) ?? 'text';
87
91
  }
88
92
  function parseNumberAttr(raw) {
89
93
  if (typeof raw === 'number' && Number.isFinite(raw))
@@ -101,39 +105,27 @@ function parseOptions(raw) {
101
105
  }
102
106
  function parseOption(raw) {
103
107
  if (typeof raw === 'string') {
104
- const label = raw.trim();
105
- return label.length > 0 ? { label, value: label } : null;
108
+ const label = trimmedNonBlank(raw);
109
+ return label ? { label, value: label } : null;
106
110
  }
107
- if (!raw || typeof raw !== 'object')
108
- return null;
109
- const obj = raw;
110
- const label = typeof obj.label === 'string' ? obj.label.trim() : '';
111
- if (label.length === 0)
111
+ const obj = asRecord(raw) ?? {};
112
+ const label = trimmedNonBlank(obj.label);
113
+ if (!label)
112
114
  return null;
113
- const value = typeof obj.value === 'string' && obj.value.trim().length > 0 ? obj.value.trim() : label;
114
- const description = typeof obj.description === 'string' && obj.description.trim().length > 0 ? obj.description.trim() : undefined;
115
- return {
116
- label,
117
- value,
118
- ...(description ? { description } : {}),
119
- };
115
+ return omitUndefined({ label, value: trimmedNonBlank(obj.value) ?? label, description: trimmedNonBlank(obj.description) });
120
116
  }
117
+ // Only `defaultValue` may hold a list; the legacy `default` key is read as a scalar only.
121
118
  function parseDefaultValue(question, options) {
122
- const raw = typeof question.defaultValue === 'string' || Array.isArray(question.defaultValue)
123
- ? question.defaultValue
124
- : typeof question.defaultValue === 'number' || typeof question.defaultValue === 'boolean'
125
- ? String(question.defaultValue)
126
- : typeof question.default === 'string'
127
- ? question.default
128
- : typeof question.default === 'number' || typeof question.default === 'boolean'
129
- ? String(question.default)
130
- : undefined;
131
- if (typeof raw === 'string')
132
- return formOptionValueForLabel({ question: { options }, labelOrValue: raw });
133
- if (Array.isArray(raw)) {
134
- return raw.filter((value) => typeof value === 'string').map((value) => formOptionValueForLabel({ question: { options }, labelOrValue: value }));
135
- }
136
- return undefined;
119
+ const resolve = (labelOrValue) => formOptionValueForLabel({ question: { options }, labelOrValue });
120
+ if (Array.isArray(question.defaultValue))
121
+ return stringsOnly(question.defaultValue).map(resolve);
122
+ const raw = scalarString(question.defaultValue) ?? scalarString(question.default);
123
+ return raw === undefined ? undefined : resolve(raw);
124
+ }
125
+ function scalarString(raw) {
126
+ if (typeof raw === 'number' || typeof raw === 'boolean')
127
+ return String(raw);
128
+ return stringOrUndefined(raw);
137
129
  }
138
130
  /** `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`. */
139
131
  const HEX_COLOR = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
@@ -170,24 +162,22 @@ export function isRenderableColor({ value }) {
170
162
  function parseDirectionCards(raw) {
171
163
  if (!Array.isArray(raw))
172
164
  return undefined;
173
- const out = [];
174
- for (const entry of raw) {
175
- if (!entry || typeof entry !== 'object')
176
- continue;
177
- const e = entry;
178
- const id = typeof e.id === 'string' && e.id.trim().length > 0 ? e.id.trim() : null;
179
- const label = typeof e.label === 'string' ? e.label : null;
180
- if (id === null || label === null)
181
- continue;
182
- const mood = typeof e.mood === 'string' ? e.mood : '';
183
- const references = Array.isArray(e.references) ? e.references.filter((r) => typeof r === 'string').slice(0, 6) : [];
184
- const palette = Array.isArray(e.palette)
185
- ? e.palette.filter((p) => typeof p === 'string' && isRenderableColor({ value: p })).slice(0, 8)
186
- : [];
187
- const displayFont = typeof e.displayFont === 'string' ? e.displayFont : 'Georgia, serif';
188
- const bodyFont = typeof e.bodyFont === 'string' ? e.bodyFont : '-apple-system, system-ui, sans-serif';
189
- out.push({ id, label, mood, references, palette, displayFont, bodyFont });
190
- }
165
+ const out = raw.flatMap((entry) => parseDirectionCard(asRecord(entry) ?? {}) ?? []);
191
166
  return out.length > 0 ? out : undefined;
192
167
  }
168
+ function parseDirectionCard(e) {
169
+ const id = trimmedNonBlank(e.id);
170
+ const label = stringOrUndefined(e.label);
171
+ if (id === undefined || label === undefined)
172
+ return null;
173
+ return {
174
+ id,
175
+ label,
176
+ mood: stringOrUndefined(e.mood) ?? '',
177
+ references: stringsOnly(e.references).slice(0, 6),
178
+ palette: stringsOnly(e.palette).filter((value) => isRenderableColor({ value })).slice(0, 8),
179
+ displayFont: stringOrUndefined(e.displayFont) ?? 'Georgia, serif',
180
+ bodyFont: stringOrUndefined(e.bodyFont) ?? '-apple-system, system-ui, sans-serif',
181
+ };
182
+ }
193
183
  //# sourceMappingURL=normalize.js.map