@happyvertical/smrt-svelte 0.55.7 → 0.55.8

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 (197) hide show
  1. package/AGENTS.md +7 -44
  2. package/README.md +112 -1
  3. package/agents/browser-ai.md +47 -0
  4. package/agents/command-palette.md +85 -0
  5. package/agents/import-export.md +62 -0
  6. package/agents/overview-surfaces.md +255 -0
  7. package/agents/screens.md +57 -0
  8. package/dist/browser-ai/adapters/tts/browser-synthesis.d.ts +5 -0
  9. package/dist/browser-ai/adapters/tts/browser-synthesis.d.ts.map +1 -1
  10. package/dist/browser-ai/adapters/tts/browser-synthesis.js +83 -55
  11. package/dist/components/command-palette/CommandPalette.svelte +75 -0
  12. package/dist/components/command-palette/CommandPalette.svelte.d.ts +22 -0
  13. package/dist/components/command-palette/CommandPalette.svelte.d.ts.map +1 -0
  14. package/dist/components/command-palette/CommandPaletteDialog.svelte +406 -0
  15. package/dist/components/command-palette/CommandPaletteDialog.svelte.d.ts +18 -0
  16. package/dist/components/command-palette/CommandPaletteDialog.svelte.d.ts.map +1 -0
  17. package/dist/components/command-palette/CommandPaletteTrigger.svelte +91 -0
  18. package/dist/components/command-palette/CommandPaletteTrigger.svelte.d.ts +17 -0
  19. package/dist/components/command-palette/CommandPaletteTrigger.svelte.d.ts.map +1 -0
  20. package/dist/components/command-palette/context.d.ts +18 -0
  21. package/dist/components/command-palette/context.d.ts.map +1 -0
  22. package/dist/components/command-palette/context.js +38 -0
  23. package/dist/components/command-palette/controller.svelte.d.ts +70 -0
  24. package/dist/components/command-palette/controller.svelte.d.ts.map +1 -0
  25. package/dist/components/command-palette/controller.svelte.js +379 -0
  26. package/dist/components/command-palette/hotkeys.d.ts +30 -0
  27. package/dist/components/command-palette/hotkeys.d.ts.map +1 -0
  28. package/dist/components/command-palette/hotkeys.js +161 -0
  29. package/dist/components/command-palette/index.d.ts +19 -0
  30. package/dist/components/command-palette/index.d.ts.map +1 -0
  31. package/dist/components/command-palette/index.js +17 -0
  32. package/dist/components/command-palette/match.d.ts +38 -0
  33. package/dist/components/command-palette/match.d.ts.map +1 -0
  34. package/dist/components/command-palette/match.js +172 -0
  35. package/dist/components/command-palette/providers.d.ts +159 -0
  36. package/dist/components/command-palette/providers.d.ts.map +1 -0
  37. package/dist/components/command-palette/providers.js +345 -0
  38. package/dist/components/command-palette/rank.d.ts +20 -0
  39. package/dist/components/command-palette/rank.d.ts.map +1 -0
  40. package/dist/components/command-palette/rank.js +74 -0
  41. package/dist/components/command-palette/types.d.ts +119 -0
  42. package/dist/components/command-palette/types.d.ts.map +1 -0
  43. package/dist/components/command-palette/types.js +10 -0
  44. package/dist/components/command-palette/use-provider.svelte.d.ts +14 -0
  45. package/dist/components/command-palette/use-provider.svelte.d.ts.map +1 -0
  46. package/dist/components/command-palette/use-provider.svelte.js +23 -0
  47. package/dist/components/import-export/ImportExport.svelte +678 -0
  48. package/dist/components/import-export/ImportExport.svelte.d.ts +5 -0
  49. package/dist/components/import-export/ImportExport.svelte.d.ts.map +1 -0
  50. package/dist/components/import-export/collection-adapter.d.ts +35 -0
  51. package/dist/components/import-export/collection-adapter.d.ts.map +1 -0
  52. package/dist/components/import-export/collection-adapter.js +42 -0
  53. package/dist/components/import-export/csv.d.ts +60 -0
  54. package/dist/components/import-export/csv.d.ts.map +1 -0
  55. package/dist/components/import-export/csv.js +192 -0
  56. package/dist/components/import-export/download.d.ts +4 -0
  57. package/dist/components/import-export/download.d.ts.map +1 -0
  58. package/dist/components/import-export/download.js +17 -0
  59. package/dist/components/import-export/export.d.ts +25 -0
  60. package/dist/components/import-export/export.d.ts.map +1 -0
  61. package/dist/components/import-export/export.js +68 -0
  62. package/dist/components/import-export/fields.d.ts +48 -0
  63. package/dist/components/import-export/fields.d.ts.map +1 -0
  64. package/dist/components/import-export/fields.js +103 -0
  65. package/dist/components/import-export/index.d.ts +12 -0
  66. package/dist/components/import-export/index.d.ts.map +1 -0
  67. package/dist/components/import-export/index.js +9 -0
  68. package/dist/components/import-export/mapping.d.ts +12 -0
  69. package/dist/components/import-export/mapping.d.ts.map +1 -0
  70. package/dist/components/import-export/mapping.js +38 -0
  71. package/dist/components/import-export/props.d.ts +34 -0
  72. package/dist/components/import-export/props.d.ts.map +1 -0
  73. package/dist/components/import-export/props.js +1 -0
  74. package/dist/components/import-export/report.d.ts +13 -0
  75. package/dist/components/import-export/report.d.ts.map +1 -0
  76. package/dist/components/import-export/report.js +50 -0
  77. package/dist/components/import-export/runner.d.ts +17 -0
  78. package/dist/components/import-export/runner.d.ts.map +1 -0
  79. package/dist/components/import-export/runner.js +39 -0
  80. package/dist/components/import-export/types.d.ts +100 -0
  81. package/dist/components/import-export/types.d.ts.map +1 -0
  82. package/dist/components/import-export/types.js +6 -0
  83. package/dist/components/import-export/validate.d.ts +39 -0
  84. package/dist/components/import-export/validate.d.ts.map +1 -0
  85. package/dist/components/import-export/validate.js +268 -0
  86. package/dist/components/overview/OverviewGrid.svelte +579 -0
  87. package/dist/components/overview/OverviewGrid.svelte.d.ts +5 -0
  88. package/dist/components/overview/OverviewGrid.svelte.d.ts.map +1 -0
  89. package/dist/components/overview/WidgetOptionsForm.svelte +193 -0
  90. package/dist/components/overview/WidgetOptionsForm.svelte.d.ts +5 -0
  91. package/dist/components/overview/WidgetOptionsForm.svelte.d.ts.map +1 -0
  92. package/dist/components/overview/controller.svelte.d.ts +92 -0
  93. package/dist/components/overview/controller.svelte.d.ts.map +1 -0
  94. package/dist/components/overview/controller.svelte.js +336 -0
  95. package/dist/components/overview/format.d.ts +13 -0
  96. package/dist/components/overview/format.d.ts.map +1 -0
  97. package/dist/components/overview/format.js +45 -0
  98. package/dist/components/overview/grid-types.d.ts +52 -0
  99. package/dist/components/overview/grid-types.d.ts.map +1 -0
  100. package/dist/components/overview/grid-types.js +1 -0
  101. package/dist/components/overview/index.d.ts +24 -0
  102. package/dist/components/overview/index.d.ts.map +1 -0
  103. package/dist/components/overview/index.js +22 -0
  104. package/dist/components/overview/load.d.ts +30 -0
  105. package/dist/components/overview/load.d.ts.map +1 -0
  106. package/dist/components/overview/load.js +92 -0
  107. package/dist/components/overview/markdown.d.ts +53 -0
  108. package/dist/components/overview/markdown.d.ts.map +1 -0
  109. package/dist/components/overview/markdown.js +203 -0
  110. package/dist/components/overview/model.d.ts +100 -0
  111. package/dist/components/overview/model.d.ts.map +1 -0
  112. package/dist/components/overview/model.js +460 -0
  113. package/dist/components/overview/recipe-widgets.d.ts +77 -0
  114. package/dist/components/overview/recipe-widgets.d.ts.map +1 -0
  115. package/dist/components/overview/recipe-widgets.js +151 -0
  116. package/dist/components/overview/registry.d.ts +45 -0
  117. package/dist/components/overview/registry.d.ts.map +1 -0
  118. package/dist/components/overview/registry.js +112 -0
  119. package/dist/components/overview/resize.d.ts +25 -0
  120. package/dist/components/overview/resize.d.ts.map +1 -0
  121. package/dist/components/overview/resize.js +41 -0
  122. package/dist/components/overview/schema.d.ts +34 -0
  123. package/dist/components/overview/schema.d.ts.map +1 -0
  124. package/dist/components/overview/schema.js +180 -0
  125. package/dist/components/overview/server.d.ts +14 -0
  126. package/dist/components/overview/server.d.ts.map +1 -0
  127. package/dist/components/overview/server.js +13 -0
  128. package/dist/components/overview/types.d.ts +235 -0
  129. package/dist/components/overview/types.d.ts.map +1 -0
  130. package/dist/components/overview/types.js +17 -0
  131. package/dist/components/overview/widgets/ChartWidget.svelte +111 -0
  132. package/dist/components/overview/widgets/ChartWidget.svelte.d.ts +6 -0
  133. package/dist/components/overview/widgets/ChartWidget.svelte.d.ts.map +1 -0
  134. package/dist/components/overview/widgets/MetricWidget.svelte +86 -0
  135. package/dist/components/overview/widgets/MetricWidget.svelte.d.ts +6 -0
  136. package/dist/components/overview/widgets/MetricWidget.svelte.d.ts.map +1 -0
  137. package/dist/components/overview/widgets/NoteWidget.svelte +61 -0
  138. package/dist/components/overview/widgets/NoteWidget.svelte.d.ts +5 -0
  139. package/dist/components/overview/widgets/NoteWidget.svelte.d.ts.map +1 -0
  140. package/dist/components/overview/widgets/RecordListWidget.svelte +91 -0
  141. package/dist/components/overview/widgets/RecordListWidget.svelte.d.ts +6 -0
  142. package/dist/components/overview/widgets/RecordListWidget.svelte.d.ts.map +1 -0
  143. package/dist/components/overview/widgets/ShortcutsWidget.svelte +80 -0
  144. package/dist/components/overview/widgets/ShortcutsWidget.svelte.d.ts +6 -0
  145. package/dist/components/overview/widgets/ShortcutsWidget.svelte.d.ts.map +1 -0
  146. package/dist/components/overview/widgets/core.d.ts +44 -0
  147. package/dist/components/overview/widgets/core.d.ts.map +1 -0
  148. package/dist/components/overview/widgets/core.js +222 -0
  149. package/dist/components/overview/widgets/data.d.ts +76 -0
  150. package/dist/components/overview/widgets/data.d.ts.map +1 -0
  151. package/dist/components/overview/widgets/data.js +40 -0
  152. package/dist/components/screens/DetailScreen.svelte +220 -0
  153. package/dist/components/screens/DetailScreen.svelte.d.ts +5 -0
  154. package/dist/components/screens/DetailScreen.svelte.d.ts.map +1 -0
  155. package/dist/components/screens/EditForm.svelte +313 -0
  156. package/dist/components/screens/EditForm.svelte.d.ts +5 -0
  157. package/dist/components/screens/EditForm.svelte.d.ts.map +1 -0
  158. package/dist/components/screens/ListScreen.svelte +191 -0
  159. package/dist/components/screens/ListScreen.svelte.d.ts +5 -0
  160. package/dist/components/screens/ListScreen.svelte.d.ts.map +1 -0
  161. package/dist/components/screens/RecipeScreens.svelte +237 -0
  162. package/dist/components/screens/RecipeScreens.svelte.d.ts +5 -0
  163. package/dist/components/screens/RecipeScreens.svelte.d.ts.map +1 -0
  164. package/dist/components/screens/fields.d.ts +54 -0
  165. package/dist/components/screens/fields.d.ts.map +1 -0
  166. package/dist/components/screens/fields.js +229 -0
  167. package/dist/components/screens/index.d.ts +8 -0
  168. package/dist/components/screens/index.d.ts.map +1 -0
  169. package/dist/components/screens/index.js +6 -0
  170. package/dist/components/screens/types.d.ts +215 -0
  171. package/dist/components/screens/types.d.ts.map +1 -0
  172. package/dist/components/screens/types.js +1 -0
  173. package/dist/components/screens/values.d.ts +62 -0
  174. package/dist/components/screens/values.d.ts.map +1 -0
  175. package/dist/components/screens/values.js +289 -0
  176. package/dist/components/sortable/controller.svelte.d.ts +10 -0
  177. package/dist/components/sortable/controller.svelte.d.ts.map +1 -1
  178. package/dist/components/sortable/controller.svelte.js +29 -7
  179. package/dist/components/workspace/manifest-nav.d.ts +20 -0
  180. package/dist/components/workspace/manifest-nav.d.ts.map +1 -1
  181. package/dist/components/workspace/manifest-nav.js +42 -13
  182. package/dist/i18n/server.d.ts +6 -0
  183. package/dist/i18n/server.d.ts.map +1 -1
  184. package/dist/i18n/server.js +6 -0
  185. package/dist/i18n/strings.import-export.d.ts +88 -0
  186. package/dist/i18n/strings.import-export.d.ts.map +1 -0
  187. package/dist/i18n/strings.import-export.js +88 -0
  188. package/dist/i18n/strings.overview.d.ts +100 -0
  189. package/dist/i18n/strings.overview.d.ts.map +1 -0
  190. package/dist/i18n/strings.overview.js +107 -0
  191. package/dist/i18n/strings.palette.d.ts +18 -0
  192. package/dist/i18n/strings.palette.d.ts.map +1 -0
  193. package/dist/i18n/strings.palette.js +18 -0
  194. package/dist/i18n/strings.screens.d.ts +31 -0
  195. package/dist/i18n/strings.screens.d.ts.map +1 -0
  196. package/dist/i18n/strings.screens.js +31 -0
  197. package/package.json +45 -16
@@ -0,0 +1,289 @@
1
+ /**
2
+ * Pure value handling for the manifest-derived record screens (#3718):
3
+ * draft <-> payload conversion for edit forms and display formatting for list
4
+ * and view screens. No DOM and no i18n: components map error codes and the
5
+ * Yes/No labels through `useI18n`.
6
+ *
7
+ * Money is integer minor units end to end. Editing shows major units, but the
8
+ * conversion is exact string math (never `* 100` on a float), and anything
9
+ * that is not a JavaScript-safe integer is rejected rather than rounded.
10
+ */
11
+ import { ISO_4217_MINOR_UNITS } from '@happyvertical/smrt-ui/currency';
12
+ /** Currency used for money fields when the screen is not told otherwise. */
13
+ export const DEFAULT_SCREEN_CURRENCY = 'USD';
14
+ /** Decimal places of a currency's minor unit (2 for an unknown code). */
15
+ export function currencyMinorDigits(currency) {
16
+ return ISO_4217_MINOR_UNITS.get(currency.trim().toUpperCase()) ?? 2;
17
+ }
18
+ /** `1999`, 2 digits -> `19.99`. Exact; `null` for a non-safe integer. */
19
+ export function minorUnitsToMajorString(minor, digits) {
20
+ if (typeof minor !== 'number' || !Number.isSafeInteger(minor))
21
+ return null;
22
+ const negative = minor < 0;
23
+ const absolute = String(Math.abs(minor));
24
+ let text;
25
+ if (digits <= 0) {
26
+ text = absolute;
27
+ }
28
+ else {
29
+ const padded = absolute.padStart(digits + 1, '0');
30
+ text = `${padded.slice(0, -digits)}.${padded.slice(-digits)}`;
31
+ }
32
+ return negative ? `-${text}` : text;
33
+ }
34
+ /**
35
+ * `19.99`, 2 digits -> `1999`. Accepts a leading sign and `,` grouping;
36
+ * rejects more fraction digits than the currency has and non-safe integers.
37
+ */
38
+ export function majorStringToMinorUnits(text, digits) {
39
+ const cleaned = text.trim().replace(/[,\s]/g, '');
40
+ const pattern = digits > 0 ? /^(-?)(\d+)(?:\.(\d*))?$/ : /^(-?)(\d+)$/;
41
+ const match = pattern.exec(cleaned);
42
+ if (!match)
43
+ return null;
44
+ const [, sign, whole, fraction = ''] = match;
45
+ if (fraction.length > Math.max(0, digits))
46
+ return null;
47
+ const minor = Number(`${whole}${fraction.padEnd(Math.max(0, digits), '0')}`);
48
+ if (!Number.isSafeInteger(minor))
49
+ return null;
50
+ return sign === '-' && minor !== 0 ? -minor : minor;
51
+ }
52
+ function pad(value) {
53
+ return String(value).padStart(2, '0');
54
+ }
55
+ /** A record datetime as a `datetime-local` input value (local time). */
56
+ export function toDatetimeLocal(value) {
57
+ if (value === null || value === undefined || value === '')
58
+ return '';
59
+ const date = value instanceof Date ? value : new Date(value);
60
+ if (Number.isNaN(date.getTime()))
61
+ return '';
62
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
63
+ }
64
+ /** One field's value as the draft representation its control edits. */
65
+ export function toDraftValue(field, value, context = {}) {
66
+ if (field.kind === 'boolean')
67
+ return value === true || value === 'true';
68
+ if (value === null || value === undefined)
69
+ return '';
70
+ switch (field.kind) {
71
+ case 'money':
72
+ return (minorUnitsToMajorString(value, currencyMinorDigits(context.currency ?? DEFAULT_SCREEN_CURRENCY)) ?? '');
73
+ case 'datetime':
74
+ return toDatetimeLocal(value);
75
+ case 'json':
76
+ return typeof value === 'string' ? value : JSON.stringify(value, null, 2);
77
+ default:
78
+ return String(value);
79
+ }
80
+ }
81
+ /** Draft for editing an existing record. */
82
+ export function draftFromRecord(fields, record, context = {}) {
83
+ const draft = {};
84
+ for (const field of fields) {
85
+ draft[field.name] = toDraftValue(field, record[field.name], context);
86
+ }
87
+ return draft;
88
+ }
89
+ /**
90
+ * Draft for a new record: the resolved policy default when one resolved
91
+ * (an explicit `null` default included), else the manifest default.
92
+ */
93
+ export function draftForCreate(fields, policy, context = {}) {
94
+ const draft = {};
95
+ for (const field of fields) {
96
+ const policyField = policy?.fields[field.name];
97
+ const seeded = policyField?.hasDefault === true
98
+ ? policyField.defaultValue
99
+ : field.definition.default;
100
+ draft[field.name] = toDraftValue(field, seeded, context);
101
+ }
102
+ return draft;
103
+ }
104
+ /**
105
+ * Convert a draft into a write payload. An empty optional text field becomes
106
+ * `null` when the field is nullable and `''` otherwise; every other empty
107
+ * optional field becomes `null`. A required empty field is an error.
108
+ */
109
+ export function parseDraft(fields, draft, context = {}) {
110
+ const values = {};
111
+ const errors = {};
112
+ const digits = currencyMinorDigits(context.currency ?? DEFAULT_SCREEN_CURRENCY);
113
+ for (const field of fields) {
114
+ const raw = draft[field.name];
115
+ if (field.kind === 'boolean') {
116
+ values[field.name] = raw === true || raw === 'true';
117
+ continue;
118
+ }
119
+ const text = typeof raw === 'string' ? raw : '';
120
+ const trimmed = text.trim();
121
+ if (trimmed === '') {
122
+ if (field.required) {
123
+ errors[field.name] = 'required';
124
+ }
125
+ else if ((field.kind === 'text' ||
126
+ field.kind === 'textarea' ||
127
+ field.kind === 'email' ||
128
+ field.kind === 'url' ||
129
+ field.kind === 'tel') &&
130
+ field.definition.nullable !== true) {
131
+ values[field.name] = '';
132
+ }
133
+ else {
134
+ values[field.name] = null;
135
+ }
136
+ continue;
137
+ }
138
+ switch (field.kind) {
139
+ case 'integer': {
140
+ const parsed = /^-?\d+$/.test(trimmed) ? Number(trimmed) : Number.NaN;
141
+ if (Number.isSafeInteger(parsed))
142
+ values[field.name] = parsed;
143
+ else
144
+ errors[field.name] = 'invalid_integer';
145
+ break;
146
+ }
147
+ case 'decimal': {
148
+ const parsed = Number(trimmed);
149
+ if (Number.isFinite(parsed))
150
+ values[field.name] = parsed;
151
+ else
152
+ errors[field.name] = 'invalid_decimal';
153
+ break;
154
+ }
155
+ case 'money': {
156
+ const minor = majorStringToMinorUnits(trimmed, digits);
157
+ if (minor === null)
158
+ errors[field.name] = 'invalid_money';
159
+ else
160
+ values[field.name] = minor;
161
+ break;
162
+ }
163
+ case 'datetime': {
164
+ const original = context.record?.[field.name];
165
+ const originalDate = original === null || original === undefined || original === ''
166
+ ? null
167
+ : original instanceof Date
168
+ ? original
169
+ : new Date(original);
170
+ if (originalDate &&
171
+ !Number.isNaN(originalDate.getTime()) &&
172
+ trimmed === toDatetimeLocal(originalDate)) {
173
+ // Unchanged at the control's precision: keep the full-precision
174
+ // original instead of truncating to the minute.
175
+ values[field.name] = originalDate.toISOString();
176
+ break;
177
+ }
178
+ const date = new Date(trimmed);
179
+ if (Number.isNaN(date.getTime()))
180
+ errors[field.name] = 'invalid_datetime';
181
+ else
182
+ values[field.name] = date.toISOString();
183
+ break;
184
+ }
185
+ case 'json': {
186
+ try {
187
+ values[field.name] = JSON.parse(trimmed);
188
+ }
189
+ catch {
190
+ errors[field.name] = 'invalid_json';
191
+ }
192
+ break;
193
+ }
194
+ case 'reference':
195
+ values[field.name] = trimmed;
196
+ break;
197
+ default:
198
+ // Free text keeps the user's whitespace.
199
+ values[field.name] = text;
200
+ }
201
+ }
202
+ return { values, errors };
203
+ }
204
+ /** A record value as display text for a list cell or a view row. */
205
+ export function formatScreenValue(field, value, context = {}) {
206
+ if (field.kind === 'boolean') {
207
+ if (value === null || value === undefined)
208
+ return '';
209
+ return value === true || value === 'true'
210
+ ? (context.yes ?? 'Yes')
211
+ : (context.no ?? 'No');
212
+ }
213
+ if (value === null || value === undefined || value === '')
214
+ return '';
215
+ try {
216
+ switch (field.kind) {
217
+ case 'integer':
218
+ return typeof value === 'number'
219
+ ? new Intl.NumberFormat(context.locale, {
220
+ maximumFractionDigits: 0,
221
+ }).format(value)
222
+ : String(value);
223
+ case 'decimal':
224
+ return typeof value === 'number'
225
+ ? new Intl.NumberFormat(context.locale, {
226
+ maximumFractionDigits: 6,
227
+ }).format(value)
228
+ : String(value);
229
+ case 'money': {
230
+ const currency = (context.currency ?? DEFAULT_SCREEN_CURRENCY)
231
+ .trim()
232
+ .toUpperCase();
233
+ const digits = currencyMinorDigits(currency);
234
+ if (typeof value !== 'number' || !Number.isSafeInteger(value)) {
235
+ return String(value);
236
+ }
237
+ return new Intl.NumberFormat(context.locale, {
238
+ style: 'currency',
239
+ currency,
240
+ }).format(value / 10 ** digits);
241
+ }
242
+ case 'datetime': {
243
+ const date = value instanceof Date ? value : new Date(value);
244
+ if (Number.isNaN(date.getTime()))
245
+ return String(value);
246
+ return new Intl.DateTimeFormat(context.locale, {
247
+ dateStyle: 'medium',
248
+ timeStyle: 'short',
249
+ ...(context.timeZone ? { timeZone: context.timeZone } : {}),
250
+ }).format(date);
251
+ }
252
+ case 'json':
253
+ return typeof value === 'string' ? value : JSON.stringify(value);
254
+ default:
255
+ return String(value);
256
+ }
257
+ }
258
+ catch {
259
+ // An unsupported currency code or locale must not break a row render.
260
+ return String(value);
261
+ }
262
+ }
263
+ /**
264
+ * Order two record values for a list column: empty values last (whichever
265
+ * way the column sorts), numbers numerically, everything else as text.
266
+ */
267
+ export function compareScreenValues(left, right, direction) {
268
+ if (!direction)
269
+ return 0;
270
+ const leftEmpty = left === null || left === undefined || left === '';
271
+ const rightEmpty = right === null || right === undefined || right === '';
272
+ if (leftEmpty && rightEmpty)
273
+ return 0;
274
+ if (leftEmpty)
275
+ return 1;
276
+ if (rightEmpty)
277
+ return -1;
278
+ let result;
279
+ if (typeof left === 'number' && typeof right === 'number') {
280
+ result = left - right;
281
+ }
282
+ else if (typeof left === 'boolean' && typeof right === 'boolean') {
283
+ result = Number(left) - Number(right);
284
+ }
285
+ else {
286
+ result = String(left).localeCompare(String(right));
287
+ }
288
+ return direction === 'asc' ? result : -result;
289
+ }
@@ -76,6 +76,15 @@ export interface SortableControllerOptions {
76
76
  * adjacent container at either end.
77
77
  */
78
78
  orientation?: () => 'horizontal' | 'vertical';
79
+ /**
80
+ * `true` for a single list laid out as a wrapping flow (a CSS grid of
81
+ * tiles): every arrow key steps one position along the reading order
82
+ * (Left/Right mirrored in right-to-left), and a pointer drop over an item
83
+ * that shares its row with others decides before/after by the horizontal
84
+ * midpoint; an item that fills the row (stacked layouts) still uses the
85
+ * vertical midpoint.
86
+ */
87
+ flow?: () => boolean;
79
88
  /**
80
89
  * Persist a move. May be async; a rejection restores the presentation and
81
90
  * is announced. The engine allows the next move only after it settles.
@@ -113,6 +122,7 @@ export declare class SortableController {
113
122
  pickup(itemId: string, mode: DragState['mode']): void;
114
123
  private announcePosition;
115
124
  private moveKeyboardTarget;
125
+ private isRtl;
116
126
  drop(): Promise<void>;
117
127
  /** Cancel the move in flight. Focus returns to the item when `restoreFocus`. */
118
128
  cancel(announce?: boolean, restoreFocus?: boolean): void;
@@ -1 +1 @@
1
- {"version":3,"file":"controller.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/sortable/controller.svelte.ts"],"names":[],"mappings":"AAaA,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,qDAAqD;AACrD,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,8EAA8E;AAC9E,MAAM,MAAM,oBAAoB,GAC5B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,GACxD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,GACD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IACE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,GACD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC,sEAAsE;AACtE,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,iEAAiE;IACjE,IAAI,EAAE,MAAM,WAAW,GAAG,SAAS,CAAC;IACpC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,UAAU,EAAE,MAAM,SAAS,qBAAqB,EAAE,CAAC;IACnD,4CAA4C;IAC5C,OAAO,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE,CAAC;IACpD,4EAA4E;IAC5E,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAClD,qEAAqE;IACrE,yBAAyB,EAAE,MAAM,OAAO,CAAC;IACzC,uEAAuE;IACvE,OAAO,EAAE,MAAM,OAAO,CAAC;IACvB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,YAAY,GAAG,UAAU,CAAC;IAC9C;;;OAGG;IACH,MAAM,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,QAAQ,EAAE,CAAC,YAAY,EAAE,oBAAoB,KAAK,MAAM,CAAC;IACzD,uEAAuE;IACvE,WAAW,CAAC,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,WAAW,GAAG,IAAI,CAAC;CAChE;AAED,UAAU,SAAS;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,gBAAgB,CAAC;IACzB,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;CAC9B;AAcD,qBAAa,kBAAkB;IAQjB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAPpC,IAAI,wBAAmC;IACvC,YAAY,SAAc;IAC1B,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,qBAAqB,CAAgC;IAC7D,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,cAAc,CAAwC;gBAEjC,OAAO,EAAE,yBAAyB;IA0B/D,wCAAwC;IACxC,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,qFAAqF;IACrF,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED,OAAO,KAAK,UAAU,GAErB;IAED,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,GAAG;IAIX,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,IAAI;IAcrD,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,kBAAkB;IA6DpB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA4C3B,gFAAgF;IAChF,MAAM,CAAC,QAAQ,UAAO,EAAE,YAAY,UAAO,GAAG,IAAI;IAYlD,OAAO,CAAC,iBAAiB;IASzB;;;;OAIG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAQrC,OAAO,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAgCnD;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAe7C,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAUjD,OAAO,IAAI,IAAI;IAIf,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAatD,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,2BAA2B;IAiCnC,yFAAyF;IACzF,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,oBAAoB;IA4B5B,SAAS,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAIpC,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAIxC,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAmBtC,OAAO,CAAC,aAAa;IAcrB,yEAAyE;IACzE,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAsBzE,wDAAwD;IACxD,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;CAc7D;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAC5B,OAAO,EAAE,yBAAyB,GACjC,kBAAkB,CAEpB"}
1
+ {"version":3,"file":"controller.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/sortable/controller.svelte.ts"],"names":[],"mappings":"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"}
@@ -112,6 +112,14 @@ export class SortableController {
112
112
  return;
113
113
  const vertical = this.options.orientation?.() === 'vertical';
114
114
  const target = { ...drag.target };
115
+ if (this.options.flow?.()) {
116
+ const mirrored = direction === 'horizontal' && this.isRtl();
117
+ const max = this.itemsIn(target.containerId, drag.itemId).length;
118
+ target.index = clamp(target.index + (mirrored ? -delta : delta), max);
119
+ this.drag = { ...drag, target };
120
+ this.announcePosition(this.drag);
121
+ return;
122
+ }
115
123
  // With same-container reordering off, the source container pins the
116
124
  // position (as it does for pointer moves): a vertical step leaves it.
117
125
  const pinned = !this.options.allowSameContainerReorder() &&
@@ -160,6 +168,12 @@ export class SortableController {
160
168
  this.drag = { ...drag, target };
161
169
  this.announcePosition(this.drag);
162
170
  }
171
+ isRtl() {
172
+ const root = this.options.root();
173
+ return (root !== undefined &&
174
+ typeof getComputedStyle === 'function' &&
175
+ getComputedStyle(root).direction === 'rtl');
176
+ }
163
177
  async drop() {
164
178
  const state = this.drag;
165
179
  if (!state)
@@ -325,7 +339,7 @@ export class SortableController {
325
339
  return (document.elementFromPoint?.(event.clientX, event.clientY) ??
326
340
  event.target);
327
341
  }
328
- setPointerTargetFromElement(element, clientY) {
342
+ setPointerTargetFromElement(element, clientY, clientX) {
329
343
  // Container ids belong to the host's domain and may repeat in another
330
344
  // instance. Pointer hit-testing must never escape this one's root.
331
345
  const root = this.options.root();
@@ -347,13 +361,21 @@ export class SortableController {
347
361
  const rawIndex = this.itemsIn(containerId).indexOf(itemElement.dataset[selectors.itemKey] ?? '');
348
362
  if (rawIndex < 0)
349
363
  return container;
350
- this.pointerTarget(containerId, this.insertionIndex(containerId, rawIndex, itemElement, clientY));
364
+ this.pointerTarget(containerId, this.insertionIndex(containerId, rawIndex, itemElement, clientY, clientX));
351
365
  return container;
352
366
  }
353
367
  /** Index to insert at when hovering item `rawIndex`, after removing the dragged item. */
354
- insertionIndex(containerId, rawIndex, itemElement, clientY) {
368
+ insertionIndex(containerId, rawIndex, itemElement, clientY, clientX) {
355
369
  const rect = itemElement.getBoundingClientRect();
356
- const before = clientY < rect.top + rect.height / 2;
370
+ let before = clientY < rect.top + rect.height / 2;
371
+ if (this.options.flow?.() && clientX !== undefined) {
372
+ const rootWidth = this.options.root()?.getBoundingClientRect().width ?? 0;
373
+ // A tile narrower than the list shares its row: order is horizontal.
374
+ if (rect.width < rootWidth * 0.95) {
375
+ const mid = rect.left + rect.width / 2;
376
+ before = this.isRtl() ? clientX > mid : clientX < mid;
377
+ }
378
+ }
357
379
  let targetIndex = rawIndex + (before ? 0 : 1);
358
380
  const drag = this.drag;
359
381
  if (drag?.source.containerId === containerId &&
@@ -375,7 +397,7 @@ export class SortableController {
375
397
  this.cancel();
376
398
  return;
377
399
  }
378
- const container = this.setPointerTargetFromElement(this.elementAtPointer(event), event.clientY);
400
+ const container = this.setPointerTargetFromElement(this.elementAtPointer(event), event.clientY, event.clientX);
379
401
  if (container?.disabled) {
380
402
  this.say({ type: 'unavailable', container: container.label });
381
403
  this.cancel(false);
@@ -405,7 +427,7 @@ export class SortableController {
405
427
  this.pointerSession = { ...session, active: true };
406
428
  }
407
429
  event.preventDefault();
408
- this.setPointerTargetFromElement(this.elementAtPointer(event), event.clientY);
430
+ this.setPointerTargetFromElement(this.elementAtPointer(event), event.clientY, event.clientX);
409
431
  }
410
432
  pointerTarget(containerId, index) {
411
433
  const drag = this.drag;
@@ -431,7 +453,7 @@ export class SortableController {
431
453
  this.cancel(false);
432
454
  return;
433
455
  }
434
- this.pointerTarget(containerId, this.insertionIndex(containerId, rawIndex, event.currentTarget, event.clientY));
456
+ this.pointerTarget(containerId, this.insertionIndex(containerId, rawIndex, event.currentTarget, event.clientY, event.clientX));
435
457
  void this.drop();
436
458
  }
437
459
  /** Native drop on a container's empty space: append. */
@@ -137,6 +137,26 @@ export interface NavTreeFromManifestOptions {
137
137
  * `navTreeFromManifest` apply it.
138
138
  */
139
139
  export declare function pluralizeClassName(name: string): string;
140
+ /** A manifest entry that passed the navigation filters, with what it exposes. */
141
+ export interface NavigableManifestEntry<E extends SmrtManifestEntryLike = SmrtManifestEntryLike> {
142
+ entry: E;
143
+ /** Qualified name, or the synthesised `package:Class`. */
144
+ qualifier: string;
145
+ /** Whether the REST `create` route is generated. */
146
+ creatable: boolean;
147
+ }
148
+ /**
149
+ * The manifest entries admin navigation would list (the same filters as
150
+ * {@link navTreeFromManifest}: no collection classes, internal/test visibility,
151
+ * routeless `api`, or STI subtypes of a shared route), so other manifest-driven
152
+ * surfaces such as the command palette agree with the nav on what exists.
153
+ * Order is manifest order; sort for display.
154
+ */
155
+ export declare function navigableManifestEntries<E extends SmrtManifestEntryLike>(manifest: {
156
+ objects: Record<string, E>;
157
+ }, options?: {
158
+ permittedResources?: readonly string[];
159
+ }): NavigableManifestEntry<E>[];
140
160
  /**
141
161
  * Walk a SMRT manifest and emit the `NavSection[]` shape that
142
162
  * `<NavTree items={...}>` and `RoleConfig.sections` consume.
@@ -1 +1 @@
1
- {"version":3,"file":"manifest-nav.d.ts","sourceRoot":"","sources":["../../../src/components/workspace/manifest-nav.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAQH;;;;GAIG;AACH,KAAK,kBAAkB,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC;AAEzD,mEAAmE;AACnE,MAAM,WAAW,qBAAqB;IACpC,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC;;;;OAIG;IACH,eAAe,CAAC,EAAE;QAChB,EAAE,CAAC,EAAE;YACH,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,KAAK,CAAC,EAAE,MAAM,CAAC;SAChB,CAAC;QACF;;;;;WAKG;QACH,GAAG,CAAC,EACA,OAAO,GACP;YAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAC;QACvE;;;WAGG;QACH,UAAU,CAAC,EAAE,kBAAkB,CAAC;QAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED,uCAAuC;AACvC,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;CAChD;AAWD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC;AAEjC,uDAAuD;AACvD,MAAM,WAAW,0BAA0B;IACzC;;;;;;;;;OASG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B;;;;;;;;;;;;;;;OAeG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAWD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAevD;AA6OD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,GAAE,0BAA+B,GACvC,UAAU,EAAE,CAkEd"}
1
+ {"version":3,"file":"manifest-nav.d.ts","sourceRoot":"","sources":["../../../src/components/workspace/manifest-nav.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAQH;;;;GAIG;AACH,KAAK,kBAAkB,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC;AAEzD,mEAAmE;AACnE,MAAM,WAAW,qBAAqB;IACpC,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC;;;;OAIG;IACH,eAAe,CAAC,EAAE;QAChB,EAAE,CAAC,EAAE;YACH,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,KAAK,CAAC,EAAE,MAAM,CAAC;SAChB,CAAC;QACF;;;;;WAKG;QACH,GAAG,CAAC,EACA,OAAO,GACP;YAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAC;QACvE;;;WAGG;QACH,UAAU,CAAC,EAAE,kBAAkB,CAAC;QAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED,uCAAuC;AACvC,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;CAChD;AAWD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC;AAEjC,uDAAuD;AACvD,MAAM,WAAW,0BAA0B;IACzC;;;;;;;;;OASG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B;;;;;;;;;;;;;;;OAeG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAWD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAevD;AAqPD,iFAAiF;AACjF,MAAM,WAAW,sBAAsB,CACrC,CAAC,SAAS,qBAAqB,GAAG,qBAAqB;IAEvD,KAAK,EAAE,CAAC,CAAC;IACT,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,SAAS,EAAE,OAAO,CAAC;CACpB;AAgBD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,qBAAqB,EACtE,QAAQ,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;CAAE,EACxC,OAAO,GAAE;IAAE,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAO,GACvD,sBAAsB,CAAC,CAAC,CAAC,EAAE,CAc7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,GAAE,0BAA+B,GACvC,UAAU,EAAE,CA4Dd"}
@@ -185,6 +185,13 @@ function entryIndexByClassName(manifest) {
185
185
  * - `{ exclude: [...] }` → all except listed routes
186
186
  */
187
187
  function hasListRoute(entry) {
188
+ return hasApiRoute(entry, 'list');
189
+ }
190
+ /**
191
+ * Whether an entry exposes the named REST route (`list`, `create`, ...), per
192
+ * the same `@smrt({ api })` shapes {@link hasListRoute} documents.
193
+ */
194
+ function hasApiRoute(entry, route) {
188
195
  const api = entry.decoratorConfig?.api;
189
196
  if (api === undefined || api === true)
190
197
  return true;
@@ -194,10 +201,10 @@ function hasListRoute(entry) {
194
201
  return true;
195
202
  const obj = api;
196
203
  if (Array.isArray(obj.include)) {
197
- return obj.include.includes('list');
204
+ return obj.include.includes(route);
198
205
  }
199
206
  if (Array.isArray(obj.exclude)) {
200
- return !obj.exclude.includes('list');
207
+ return !obj.exclude.includes(route);
201
208
  }
202
209
  return true;
203
210
  }
@@ -282,6 +289,38 @@ function expandPermittedThroughStiParents(permitted, manifest) {
282
289
  }
283
290
  return expanded;
284
291
  }
292
+ function* listNavigableManifestEntries(manifest, permitted) {
293
+ for (const entry of Object.values(manifest.objects)) {
294
+ if (looksLikeCollectionClass(entry))
295
+ continue;
296
+ if (!isPublicEntry(entry))
297
+ continue;
298
+ if (!hasListRoute(entry))
299
+ continue;
300
+ if (looksLikeStiSubtypeOfParentRoute(entry, manifest))
301
+ continue;
302
+ if (permitted && !permitted.has(entryQualifier(entry)))
303
+ continue;
304
+ yield entry;
305
+ }
306
+ }
307
+ /**
308
+ * The manifest entries admin navigation would list (the same filters as
309
+ * {@link navTreeFromManifest}: no collection classes, internal/test visibility,
310
+ * routeless `api`, or STI subtypes of a shared route), so other manifest-driven
311
+ * surfaces such as the command palette agree with the nav on what exists.
312
+ * Order is manifest order; sort for display.
313
+ */
314
+ export function navigableManifestEntries(manifest, options = {}) {
315
+ const permitted = options.permittedResources
316
+ ? expandPermittedThroughStiParents(new Set(options.permittedResources), manifest)
317
+ : undefined;
318
+ return [...listNavigableManifestEntries(manifest, permitted)].map((entry) => ({
319
+ entry: entry,
320
+ qualifier: entryQualifier(entry),
321
+ creatable: hasApiRoute(entry, 'create'),
322
+ }));
323
+ }
285
324
  /**
286
325
  * Walk a SMRT manifest and emit the `NavSection[]` shape that
287
326
  * `<NavTree items={...}>` and `RoleConfig.sections` consume.
@@ -360,18 +399,8 @@ export function navTreeFromManifest(manifest, options = {}) {
360
399
  // order, but we sort the final output anyway so callers don't have to
361
400
  // care about it.
362
401
  const sectionMap = new Map();
363
- for (const entry of Object.values(manifest.objects)) {
364
- if (looksLikeCollectionClass(entry))
365
- continue;
366
- if (!isPublicEntry(entry))
367
- continue;
368
- if (!hasListRoute(entry))
369
- continue;
370
- if (looksLikeStiSubtypeOfParentRoute(entry, manifest))
371
- continue;
402
+ for (const entry of listNavigableManifestEntries(manifest, permitted)) {
372
403
  const qualifier = entryQualifier(entry);
373
- if (permitted && !permitted.has(qualifier))
374
- continue;
375
404
  const ui = entry.decoratorConfig?.ui;
376
405
  const label = ui?.label ?? pluralizeClassName(entry.className);
377
406
  const slug = entry.collection ?? classNameToKebab(entry.className);
@@ -22,7 +22,13 @@ import './strings.forms.js';
22
22
  import './strings.board.js';
23
23
  import './strings.sortable.js';
24
24
  import './strings.audit.js';
25
+ import './strings.import-export.js';
25
26
  import './strings.workspace.js';
27
+ import './strings.screens.js';
28
+ import './strings.palette.js';
29
+ import './strings.overview.js';
30
+ import './strings.settings.js';
31
+ import './strings.browser-ai.js';
26
32
  import '../mcp-apps/strings.js';
27
33
  export interface BuildI18nSnapshotOptions {
28
34
  /** Target locale (BCP-47-ish, e.g. `fr-CA`). */
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/i18n/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAGL,KAAK,4BAA4B,EAElC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,6BAA6B,CAAC;AAMrC,OAAO,6BAA6B,CAAC;AACrC,OAAO,oBAAoB,CAAC;AAC5B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,wBAAwB,CAAC;AAChC,OAAO,wBAAwB,CAAC;AAEhC,MAAM,WAAW,wBAAwB;IACvC,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,4BAA4B,CAAC,UAAU,CAAC,CAAC;IACpD;;;OAGG;IACH,EAAE,CAAC,EAAE,4BAA4B,CAAC,IAAI,CAAC,CAAC;IACxC;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,YAAY,CAAC,CA4BvB"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/i18n/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAGL,KAAK,4BAA4B,EAElC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,6BAA6B,CAAC;AAMrC,OAAO,6BAA6B,CAAC;AACrC,OAAO,oBAAoB,CAAC;AAC5B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,4BAA4B,CAAC;AACpC,OAAO,wBAAwB,CAAC;AAChC,OAAO,sBAAsB,CAAC;AAC9B,OAAO,sBAAsB,CAAC;AAC9B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,yBAAyB,CAAC;AACjC,OAAO,wBAAwB,CAAC;AAEhC,MAAM,WAAW,wBAAwB;IACvC,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,4BAA4B,CAAC,UAAU,CAAC,CAAC;IACpD;;;OAGG;IACH,EAAE,CAAC,EAAE,4BAA4B,CAAC,IAAI,CAAC,CAAC;IACxC;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,YAAY,CAAC,CA4BvB"}
@@ -27,7 +27,13 @@ import './strings.forms.js';
27
27
  import './strings.board.js';
28
28
  import './strings.sortable.js';
29
29
  import './strings.audit.js';
30
+ import './strings.import-export.js';
30
31
  import './strings.workspace.js';
32
+ import './strings.screens.js';
33
+ import './strings.palette.js';
34
+ import './strings.overview.js';
35
+ import './strings.settings.js';
36
+ import './strings.browser-ai.js';
31
37
  import '../mcp-apps/strings.js';
32
38
  /**
33
39
  * Resolve a per-locale dictionary of message **templates** for the keys an app