@mulmoclaude/collection-plugin 0.5.8 → 0.5.9

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 (106) hide show
  1. package/dist/style.css +3 -250
  2. package/dist/vue/collectionViewMode.d.ts +1 -1
  3. package/dist/vue/collectionViewMode.d.ts.map +1 -1
  4. package/dist/vue/components/CollectionCalendarView.vue.d.ts +1 -2
  5. package/dist/vue/components/CollectionCalendarView.vue.d.ts.map +1 -1
  6. package/dist/vue/components/CollectionCustomView.vue.d.ts +1 -1
  7. package/dist/vue/components/CollectionCustomView.vue.d.ts.map +1 -1
  8. package/dist/vue/components/CollectionDayView.vue.d.ts +1 -2
  9. package/dist/vue/components/CollectionDayView.vue.d.ts.map +1 -1
  10. package/dist/vue/components/CollectionEmbedView.vue.d.ts +1 -1
  11. package/dist/vue/components/CollectionEmbedView.vue.d.ts.map +1 -1
  12. package/dist/vue/components/CollectionKanbanView.vue.d.ts +1 -2
  13. package/dist/vue/components/CollectionKanbanView.vue.d.ts.map +1 -1
  14. package/dist/vue/components/CollectionRecordPanel.vue.d.ts +5 -6
  15. package/dist/vue/components/CollectionRecordPanel.vue.d.ts.map +1 -1
  16. package/dist/vue/components/CollectionView.vue.d.ts.map +1 -1
  17. package/dist/vue/components/CollectionViewConfigModal.vue.d.ts +1 -1
  18. package/dist/vue/components/CollectionViewConfigModal.vue.d.ts.map +1 -1
  19. package/dist/vue/index.d.ts +1 -1
  20. package/dist/vue/index.d.ts.map +1 -1
  21. package/dist/vue/uiContext.d.ts +1 -2
  22. package/dist/vue/uiContext.d.ts.map +1 -1
  23. package/dist/vue/useCollectionRendering.d.ts +1 -2
  24. package/dist/vue/useCollectionRendering.d.ts.map +1 -1
  25. package/dist/vue.cjs +86 -66
  26. package/dist/vue.cjs.map +1 -1
  27. package/dist/vue.js +3 -4
  28. package/dist/vue.js.map +1 -1
  29. package/package.json +7 -17
  30. package/dist/chunk-CKQMccvm.cjs +0 -28
  31. package/dist/core/actionVisible.d.ts +0 -35
  32. package/dist/core/actionVisible.d.ts.map +0 -1
  33. package/dist/core/calendarGrid.d.ts +0 -121
  34. package/dist/core/calendarGrid.d.ts.map +0 -1
  35. package/dist/core/deriveAll.d.ts +0 -39
  36. package/dist/core/deriveAll.d.ts.map +0 -1
  37. package/dist/core/derivedFormula.d.ts +0 -19
  38. package/dist/core/derivedFormula.d.ts.map +0 -1
  39. package/dist/core/draft.d.ts +0 -19
  40. package/dist/core/draft.d.ts.map +0 -1
  41. package/dist/core/enumColors.d.ts +0 -34
  42. package/dist/core/enumColors.d.ts.map +0 -1
  43. package/dist/core/errorMessage.d.ts +0 -5
  44. package/dist/core/errorMessage.d.ts.map +0 -1
  45. package/dist/core/itemLabel.d.ts +0 -13
  46. package/dist/core/itemLabel.d.ts.map +0 -1
  47. package/dist/core/presentCollection.d.ts +0 -14
  48. package/dist/core/presentCollection.d.ts.map +0 -1
  49. package/dist/core/promptSafety.d.ts +0 -2
  50. package/dist/core/promptSafety.d.ts.map +0 -1
  51. package/dist/core/schema.d.ts +0 -356
  52. package/dist/core/schema.d.ts.map +0 -1
  53. package/dist/core/shortHexId.d.ts +0 -9
  54. package/dist/core/shortHexId.d.ts.map +0 -1
  55. package/dist/core/sortItems.d.ts +0 -30
  56. package/dist/core/sortItems.d.ts.map +0 -1
  57. package/dist/core/uiTypes.d.ts +0 -107
  58. package/dist/core/uiTypes.d.ts.map +0 -1
  59. package/dist/deriveAll-BHcs1erT.js +0 -342
  60. package/dist/deriveAll-BHcs1erT.js.map +0 -1
  61. package/dist/deriveAll-BJ0Lvm1Q.cjs +0 -359
  62. package/dist/deriveAll-BJ0Lvm1Q.cjs.map +0 -1
  63. package/dist/index.cjs +0 -57
  64. package/dist/index.d.ts +0 -15
  65. package/dist/index.d.ts.map +0 -1
  66. package/dist/index.js +0 -4
  67. package/dist/promptSafety-B0pPJkfQ.cjs +0 -1020
  68. package/dist/promptSafety-B0pPJkfQ.cjs.map +0 -1
  69. package/dist/promptSafety-DIQ2yDca.js +0 -739
  70. package/dist/promptSafety-DIQ2yDca.js.map +0 -1
  71. package/dist/schema-5P5ZJPrY.cjs +0 -42
  72. package/dist/schema-5P5ZJPrY.cjs.map +0 -1
  73. package/dist/schema-BcnitlL8.js +0 -25
  74. package/dist/schema-BcnitlL8.js.map +0 -1
  75. package/dist/server/atomic.d.ts +0 -2
  76. package/dist/server/atomic.d.ts.map +0 -1
  77. package/dist/server/delete.d.ts +0 -39
  78. package/dist/server/delete.d.ts.map +0 -1
  79. package/dist/server/derive.d.ts +0 -9
  80. package/dist/server/derive.d.ts.map +0 -1
  81. package/dist/server/discoveredCollection.d.ts +0 -19
  82. package/dist/server/discoveredCollection.d.ts.map +0 -1
  83. package/dist/server/discovery.d.ts +0 -228
  84. package/dist/server/discovery.d.ts.map +0 -1
  85. package/dist/server/host.d.ts +0 -78
  86. package/dist/server/host.d.ts.map +0 -1
  87. package/dist/server/index.d.ts +0 -12
  88. package/dist/server/index.d.ts.map +0 -1
  89. package/dist/server/io.d.ts +0 -115
  90. package/dist/server/io.d.ts.map +0 -1
  91. package/dist/server/paths.d.ts +0 -53
  92. package/dist/server/paths.d.ts.map +0 -1
  93. package/dist/server/spawn.d.ts +0 -56
  94. package/dist/server/spawn.d.ts.map +0 -1
  95. package/dist/server/templatePath.d.ts +0 -26
  96. package/dist/server/templatePath.d.ts.map +0 -1
  97. package/dist/server/util.d.ts +0 -4
  98. package/dist/server/util.d.ts.map +0 -1
  99. package/dist/server/validate.d.ts +0 -20
  100. package/dist/server/validate.d.ts.map +0 -1
  101. package/dist/server/views.d.ts +0 -21
  102. package/dist/server/views.d.ts.map +0 -1
  103. package/dist/server.cjs +0 -1759
  104. package/dist/server.cjs.map +0 -1
  105. package/dist/server.js +0 -1709
  106. package/dist/server.js.map +0 -1
@@ -1,1020 +0,0 @@
1
- //#region src/core/presentCollection.ts
2
- var TOOL_NAME = "presentCollection";
3
- var TOOL_DEFINITION = {
4
- type: "function",
5
- name: TOOL_NAME,
6
- description: "Display a schema-driven collection inline in the chat as an interactive, editable card. Shows the collection's list of records. Pass `itemId` to open one specific record on mount.",
7
- parameters: {
8
- type: "object",
9
- properties: {
10
- collectionSlug: {
11
- type: "string",
12
- description: "The slug of the collection to display (e.g. 'clients', 'invoices', 'contacts')."
13
- },
14
- itemId: {
15
- type: "string",
16
- description: "Optional primary-key value of a single record to open in detail view on mount. Omit to show the full list."
17
- }
18
- },
19
- required: ["collectionSlug"]
20
- },
21
- prompt: `After making changes to schema-driven collections, use ${TOOL_NAME} to present either the collection or the item`
22
- };
23
- var executePresentCollection = async (_context, args) => {
24
- const collectionSlug = typeof args?.collectionSlug === "string" ? args.collectionSlug.trim() : "";
25
- if (!collectionSlug) return {
26
- message: "presentCollection error: collectionSlug is required",
27
- instructions: "Tell the user you couldn't display the collection because no collection was specified, and ask which collection they mean."
28
- };
29
- const itemId = typeof args.itemId === "string" && args.itemId.trim().length > 0 ? args.itemId.trim() : void 0;
30
- const data = itemId ? {
31
- collectionSlug,
32
- itemId
33
- } : { collectionSlug };
34
- return {
35
- message: `Presented collection ${itemId ? `${collectionSlug} / ${itemId}` : collectionSlug}`,
36
- data,
37
- jsonData: data,
38
- instructions: "The collection has been presented to the user as an interactive card. They can browse, open, edit, create, and delete records directly. No further action is needed unless they ask."
39
- };
40
- };
41
- //#endregion
42
- //#region src/core/enumColors.ts
43
- var PALETTE = [
44
- {
45
- card: "border-indigo-200 bg-indigo-50 text-indigo-600 hover:bg-indigo-100",
46
- dot: "bg-indigo-500",
47
- badge: "bg-indigo-100 text-indigo-700",
48
- border: "border-indigo-200"
49
- },
50
- {
51
- card: "border-sky-200 bg-sky-50 text-sky-600 hover:bg-sky-100",
52
- dot: "bg-sky-500",
53
- badge: "bg-sky-100 text-sky-700",
54
- border: "border-sky-200"
55
- },
56
- {
57
- card: "border-cyan-200 bg-cyan-50 text-cyan-600 hover:bg-cyan-100",
58
- dot: "bg-cyan-500",
59
- badge: "bg-cyan-100 text-cyan-700",
60
- border: "border-cyan-200"
61
- },
62
- {
63
- card: "border-teal-200 bg-teal-50 text-teal-600 hover:bg-teal-100",
64
- dot: "bg-teal-500",
65
- badge: "bg-teal-100 text-teal-700",
66
- border: "border-teal-200"
67
- },
68
- {
69
- card: "border-emerald-200 bg-emerald-50 text-emerald-600 hover:bg-emerald-100",
70
- dot: "bg-emerald-500",
71
- badge: "bg-emerald-100 text-emerald-700",
72
- border: "border-emerald-200"
73
- },
74
- {
75
- card: "border-lime-200 bg-lime-50 text-lime-600 hover:bg-lime-100",
76
- dot: "bg-lime-500",
77
- badge: "bg-lime-100 text-lime-700",
78
- border: "border-lime-200"
79
- },
80
- {
81
- card: "border-violet-200 bg-violet-50 text-violet-600 hover:bg-violet-100",
82
- dot: "bg-violet-500",
83
- badge: "bg-violet-100 text-violet-700",
84
- border: "border-violet-200"
85
- },
86
- {
87
- card: "border-fuchsia-200 bg-fuchsia-50 text-fuchsia-600 hover:bg-fuchsia-100",
88
- dot: "bg-fuchsia-500",
89
- badge: "bg-fuchsia-100 text-fuchsia-700",
90
- border: "border-fuchsia-200"
91
- }
92
- ];
93
- /** Neutral styling for the empty / Uncategorized bucket — never a palette
94
- * colour, so an unset or unknown value reads grey across every surface. */
95
- var ENUM_NEUTRAL = {
96
- card: "border-slate-200 bg-slate-50 text-slate-500 hover:bg-slate-100",
97
- dot: "bg-slate-300",
98
- badge: "bg-slate-100 text-slate-500",
99
- border: "border-slate-200"
100
- };
101
- /** The urgent notification colour (red), matching the bell's `urgent`
102
- * severity. The first value a schema's `notifyWhen` flags reads this. */
103
- var ENUM_ALERT = {
104
- card: "border-red-200 bg-red-50 text-red-600 hover:bg-red-100",
105
- dot: "bg-red-500",
106
- badge: "bg-red-100 text-red-700",
107
- border: "border-red-200"
108
- };
109
- /** The nudge notification colour (amber), matching the bell's `nudge`
110
- * severity. Flagged `notifyWhen` values after the first read this. */
111
- var ENUM_NUDGE = {
112
- card: "border-amber-200 bg-amber-50 text-amber-600 hover:bg-amber-100",
113
- dot: "bg-amber-500",
114
- badge: "bg-amber-100 text-amber-700",
115
- border: "border-amber-200"
116
- };
117
- /** Classes for the enum value at `index` in its field's `values` array. A
118
- * negative index (value unset or not among the declared values) reads
119
- * neutral. */
120
- function enumColorClasses(index) {
121
- if (index < 0) return ENUM_NEUTRAL;
122
- return PALETTE[index % PALETTE.length] ?? ENUM_NEUTRAL;
123
- }
124
- /** Index of `value` within an enum field's declared `values`, or -1 when the
125
- * value is empty / unknown (→ neutral). */
126
- function enumValueIndex(values, value) {
127
- if (value === void 0 || value === null || value === "") return -1;
128
- return values?.indexOf(String(value)) ?? -1;
129
- }
130
- /** The flagged values when `fieldKey` is the schema's `notifyWhen` target (the
131
- * "notification enum"); undefined for every other field. */
132
- function notifyValuesFor(schema, fieldKey) {
133
- const spec = schema.notifyWhen;
134
- return spec && spec.field === fieldKey ? spec.in : void 0;
135
- }
136
- /** Resolve a value's colour for enum field `fieldKey`:
137
- * - Notification enum (`notifyWhen` targets it): the first flagged value (in
138
- * `notifyWhen.in` order, the most urgent) reads notification red, the rest
139
- * amber, and every non-flagged value neutral grey.
140
- * - Any other enum: the standard palette by the value's declared index. */
141
- function resolveEnumColor(schema, fieldKey, value) {
142
- const notifyValues = notifyValuesFor(schema, fieldKey);
143
- if (notifyValues) {
144
- const str = value === void 0 || value === null ? "" : String(value);
145
- const rank = notifyValues.indexOf(str);
146
- if (rank < 0) return ENUM_NEUTRAL;
147
- return rank === 0 ? ENUM_ALERT : ENUM_NUDGE;
148
- }
149
- return enumColorClasses(enumValueIndex(schema.fields[fieldKey]?.values, value));
150
- }
151
- //#endregion
152
- //#region src/core/actionVisible.ts
153
- /** Core matcher:
154
- * - no `when` ⇒ always true (visible);
155
- * - otherwise true only when `record[when.field]` is present and its
156
- * stringified value is one of `when.in`.
157
- * A missing/undefined/null field is treated as "not a match"
158
- * (hidden), so a status-gated target never shows on a record that
159
- * lacks the status. */
160
- function whenMatches(when, record) {
161
- if (!when) return true;
162
- const value = record[when.field];
163
- if (value === void 0 || value === null) return false;
164
- return when.in.includes(String(value));
165
- }
166
- /** True when the action's button should render against `record`
167
- * (see whenMatches). */
168
- function actionVisible(action, record) {
169
- return whenMatches(action.when, record);
170
- }
171
- /** True when the field should render against `record`. A field with
172
- * no `when` is always shown; otherwise it's shown only when the
173
- * record matches (e.g. hide a rating field until `visited` is true).
174
- * Purely presentational — a hidden field's stored value is never
175
- * altered, so toggling the gate back on restores it. */
176
- function fieldVisible(field, record) {
177
- return whenMatches(field.when, record);
178
- }
179
- //#endregion
180
- //#region src/core/draft.ts
181
- /** A fresh, empty row draft for a `table` field's sub-schema. */
182
- function emptyRow(subFields) {
183
- const text = {};
184
- const bool = {};
185
- const boolOriginallyPresent = {};
186
- const boolTouched = {};
187
- for (const [subKey, subField] of Object.entries(subFields)) if (subField.type === "boolean") {
188
- bool[subKey] = false;
189
- boolOriginallyPresent[subKey] = false;
190
- boolTouched[subKey] = false;
191
- } else text[subKey] = "";
192
- return {
193
- text,
194
- bool,
195
- boolOriginallyPresent,
196
- boolTouched
197
- };
198
- }
199
- /** Build a row draft from an existing persisted row. */
200
- function rowFromItem(item, subFields) {
201
- const text = {};
202
- const bool = {};
203
- const boolOriginallyPresent = {};
204
- const boolTouched = {};
205
- for (const [subKey, subField] of Object.entries(subFields)) {
206
- const raw = item[subKey];
207
- if (subField.type === "boolean") {
208
- bool[subKey] = raw === true;
209
- boolOriginallyPresent[subKey] = typeof raw === "boolean";
210
- boolTouched[subKey] = false;
211
- } else text[subKey] = raw === void 0 || raw === null ? "" : String(raw);
212
- }
213
- return {
214
- text,
215
- bool,
216
- boolOriginallyPresent,
217
- boolTouched
218
- };
219
- }
220
- /** Decide whether a boolean field's draft value should be emitted (vs.
221
- * omitted so a downstream default applies). */
222
- function shouldEmitBoolean(state, key, field) {
223
- return Boolean(state.boolOriginallyPresent[key] || state.boolTouched[key] || field.required);
224
- }
225
- /** Convert a scalar draft slot to its persisted form. `undefined` = omit. */
226
- function scalarDraftToValue(raw, fieldType) {
227
- if (raw === void 0 || raw === "") return void 0;
228
- if (fieldType === "number" || fieldType === "money") {
229
- const num = Number(raw);
230
- return Number.isFinite(num) ? num : raw;
231
- }
232
- return raw;
233
- }
234
- /** Convert one table row draft to its persisted record. */
235
- function rowDraftToRecord(rowDraft, subFields) {
236
- const row = {};
237
- for (const [subKey, subField] of Object.entries(subFields)) {
238
- if (subField.type === "boolean") {
239
- const value = rowDraft.bool[subKey] === true;
240
- if (rowDraft.boolOriginallyPresent[subKey] || rowDraft.boolTouched[subKey] || value || subField.required) row[subKey] = value;
241
- continue;
242
- }
243
- const value = scalarDraftToValue(rowDraft.text[subKey], subField.type);
244
- if (value !== void 0) row[subKey] = value;
245
- }
246
- return row;
247
- }
248
- /** Convert a full edit draft to the record to persist. */
249
- function draftToRecord(state, schema) {
250
- const record = {};
251
- for (const [key, field] of Object.entries(schema.fields)) {
252
- if (field.type === "derived" || field.type === "embed" || field.type === "toggle") continue;
253
- if (field.type === "boolean") {
254
- if (shouldEmitBoolean(state, key, field)) record[key] = state.bool[key] === true;
255
- continue;
256
- }
257
- if (field.type === "table" && field.of) {
258
- const subFields = field.of;
259
- record[key] = (state.table[key] ?? []).map((rowDraft) => rowDraftToRecord(rowDraft, subFields));
260
- continue;
261
- }
262
- const value = scalarDraftToValue(state.text[key], field.type);
263
- if (value !== void 0) record[key] = value;
264
- }
265
- return record;
266
- }
267
- /** Normalise a raw inline-edit input (table-cell checkbox/select) to its
268
- * persisted form. Mirrors `draftToRecord`'s boolean strictness; an empty
269
- * enum selection (the placeholder option) clears the field via `undefined`. */
270
- function coerceInlineValue(field, raw) {
271
- if (field.type === "boolean") return raw === true;
272
- return raw === "" ? void 0 : raw;
273
- }
274
- /** Build the full record to PUT for a single-cell inline edit, without
275
- * mutating `item`. A `undefined` value omits the key (enum cleared),
276
- * matching `draftToRecord`'s omission semantics. */
277
- function buildUpdatedRecord(item, key, value) {
278
- if (value === void 0) {
279
- const { [key]: __omit, ...rest } = item;
280
- return rest;
281
- }
282
- return {
283
- ...item,
284
- [key]: value
285
- };
286
- }
287
- /** Empty for required-field validation — NOT a truthiness check (a
288
- * numeric `0` is a filled value). */
289
- function isMissingDraftValue(value) {
290
- return value === void 0 || value === null || value === "";
291
- }
292
- /** Label of the first required table sub-field empty in any row, else null. */
293
- function firstMissingTableSubField(field, rows) {
294
- if (!field.of || !rows) return null;
295
- for (let rowIdx = 0; rowIdx < rows.length; rowIdx++) {
296
- const row = rows[rowIdx];
297
- for (const [subKey, subField] of Object.entries(field.of)) {
298
- if (!subField.required || subField.type === "boolean") continue;
299
- if (isMissingDraftValue(row.text[subKey])) return `${field.label} #${rowIdx + 1}: ${subField.label}`;
300
- }
301
- }
302
- return null;
303
- }
304
- function validateOneField(key, field, draft, record) {
305
- if (!fieldVisible(field, record)) return null;
306
- if (field.type === "table" && field.of) {
307
- const rows = draft.table[key];
308
- if (field.required && (!rows || rows.length === 0)) return field.label;
309
- return firstMissingTableSubField(field, rows);
310
- }
311
- if (!field.required) return null;
312
- if (draft.mode === "create" && field.primary === true) return null;
313
- if (field.type === "boolean" || field.type === "derived" || field.type === "embed" || field.type === "toggle") return null;
314
- return isMissingDraftValue(draft.text[key]) ? field.label : null;
315
- }
316
- /** Human-readable label of the first missing required field, or null. */
317
- function firstMissingRequiredField(draft, schema) {
318
- const record = draftToRecord(draft, schema);
319
- for (const [key, field] of Object.entries(schema.fields)) {
320
- const missing = validateOneField(key, field, draft, record);
321
- if (missing) return missing;
322
- }
323
- return null;
324
- }
325
- //#endregion
326
- //#region src/core/sortItems.ts
327
- var EMPTY = { empty: true };
328
- /** Field types that render no value text in the table, so offer no sort. */
329
- var NON_SORTABLE = new Set([
330
- "markdown",
331
- "table",
332
- "image",
333
- "file",
334
- "embed"
335
- ]);
336
- function isSortableField(field) {
337
- return !NON_SORTABLE.has(field.type);
338
- }
339
- /** Cycle one column's state: none → asc → desc → none. */
340
- function nextSortDirection(current) {
341
- if (current === null) return "asc";
342
- if (current === "asc") return "desc";
343
- return null;
344
- }
345
- function numericSortValue(raw) {
346
- if (raw == null || raw === "") return EMPTY;
347
- const num = typeof raw === "number" ? raw : Number(raw);
348
- return Number.isFinite(num) ? {
349
- empty: false,
350
- num
351
- } : EMPTY;
352
- }
353
- function stringSortValue(raw) {
354
- if (raw == null) return EMPTY;
355
- const str = String(raw);
356
- return str.trim() === "" ? EMPTY : {
357
- empty: false,
358
- str
359
- };
360
- }
361
- function dateSortValue(raw) {
362
- if (raw == null || raw === "") return EMPTY;
363
- const epoch = Date.parse(String(raw));
364
- return Number.isNaN(epoch) ? stringSortValue(raw) : {
365
- empty: false,
366
- num: epoch
367
- };
368
- }
369
- /** Enum sorts by the value's index in the declared `values` list. A value
370
- * outside the list (or unset) is treated as empty → last. */
371
- function enumSortValue(values, raw) {
372
- if (raw == null || raw === "") return EMPTY;
373
- const idx = values ? values.indexOf(String(raw)) : -1;
374
- return idx < 0 ? EMPTY : {
375
- empty: false,
376
- num: idx
377
- };
378
- }
379
- /** Boolean / toggle: false < true, never empty (unset reads as false). */
380
- function boolSortValue(checked) {
381
- return {
382
- empty: false,
383
- num: checked ? 1 : 0
384
- };
385
- }
386
- function compareSortValues(left, right) {
387
- if (left.num !== void 0 && right.num !== void 0) return left.num - right.num;
388
- const leftStr = left.str ?? String(left.num ?? "");
389
- const rightStr = right.str ?? String(right.num ?? "");
390
- return leftStr.localeCompare(rightStr);
391
- }
392
- /** Stable sort of `items` by `valueOf`. Empties always last; ties hold
393
- * source order. Returns a new array (does not mutate `items`). */
394
- function sortItems(items, direction, valueOf) {
395
- const dir = direction === "asc" ? 1 : -1;
396
- return items.map((item, index) => ({
397
- item,
398
- index,
399
- sv: valueOf(item)
400
- })).sort((left, right) => {
401
- if (left.sv.empty || right.sv.empty) {
402
- if (left.sv.empty && right.sv.empty) return left.index - right.index;
403
- return left.sv.empty ? 1 : -1;
404
- }
405
- const base = compareSortValues(left.sv, right.sv);
406
- return base !== 0 ? base * dir : left.index - right.index;
407
- }).map((decorated) => decorated.item);
408
- }
409
- //#endregion
410
- //#region src/core/itemLabel.ts
411
- var LABEL_FIELD_TYPES = new Set([
412
- "string",
413
- "text",
414
- "markdown",
415
- "email"
416
- ]);
417
- /** Which field labels a record: the schema's explicit `displayField`, else the
418
- * first non-primary text-like field (so a collection without a displayField
419
- * shows e.g. the title rather than the opaque primaryKey), else null → the
420
- * caller falls back to the primary-key value. */
421
- function labelFieldFor(schema) {
422
- if (schema.displayField) return schema.displayField;
423
- for (const [key, spec] of Object.entries(schema.fields)) if (key !== schema.primaryKey && LABEL_FIELD_TYPES.has(spec.type)) return key;
424
- return null;
425
- }
426
- /** A record's primary-key value as a string. */
427
- function itemIdOf(item, schema) {
428
- return String(item[schema.primaryKey] ?? "");
429
- }
430
- /** A record's display label: the resolved `labelField` value, else the
431
- * primary key. Pass the result of `labelFieldFor(schema)` as `labelField`
432
- * (compute it once per render rather than per item). */
433
- function itemLabelOf(item, schema, labelField) {
434
- if (labelField) {
435
- const value = item[labelField];
436
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
437
- const text = String(value);
438
- if (text.length > 0) return text;
439
- }
440
- }
441
- return itemIdOf(item, schema);
442
- }
443
- //#endregion
444
- //#region src/core/calendarGrid.ts
445
- var MS_PER_DAY = 864e5;
446
- var ISO_DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
447
- var TWO_DIGIT_RE = /^\d{2}$/;
448
- var CLOCK_RE = /(\d{1,2}):(\d{2})/g;
449
- var RANGE_SEP_RE = /[-–—~〜~]/;
450
- /** Minutes in a full day — the timeline's vertical extent. */
451
- var MINUTES_PER_DAY = 1440;
452
- function pad2(value) {
453
- return String(value).padStart(2, "0");
454
- }
455
- /** Canonical `YYYY-MM-DD` string for a civil date. */
456
- function ymdKey(ymd) {
457
- return `${String(ymd.year).padStart(4, "0")}-${pad2(ymd.month)}-${pad2(ymd.day)}`;
458
- }
459
- /** Strictly parse a `YYYY-MM-DD` string into a civil date, rejecting
460
- * anything that isn't a real calendar day (e.g. `2026-02-30`, `2026-13-01`).
461
- * Returns null for non-strings and malformed values so callers can route
462
- * records with no usable date into the "no date" tray rather than crash. */
463
- function parseIsoDate(value) {
464
- if (typeof value !== "string") return null;
465
- const match = ISO_DATE_RE.exec(value.trim());
466
- if (!match) return null;
467
- const year = Number(match[1]);
468
- const month = Number(match[2]);
469
- const day = Number(match[3]);
470
- const probe = new Date(Date.UTC(year, month - 1, day));
471
- if (probe.getUTCFullYear() !== year || probe.getUTCMonth() !== month - 1 || probe.getUTCDate() !== day) return null;
472
- return {
473
- year,
474
- month,
475
- day
476
- };
477
- }
478
- /** Minutes-of-day for an `HH:MM` pair, or null when out of range. */
479
- function clockToMinutes(hours, minutes) {
480
- if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null;
481
- return hours * 60 + minutes;
482
- }
483
- /** Strictly parse a `YYYY-MM-DDTHH:MM` (optional `:SS`) datetime into its
484
- * civil date and minutes-of-day. Returns null for anything that isn't a real
485
- * calendar day or a valid 24h clock. */
486
- function parseIsoDateTime(value) {
487
- if (typeof value !== "string") return null;
488
- const trimmed = value.trim();
489
- const tIndex = trimmed.indexOf("T");
490
- if (tIndex === -1) return null;
491
- const ymd = parseIsoDate(trimmed.slice(0, tIndex));
492
- if (!ymd) return null;
493
- const parts = trimmed.slice(tIndex + 1).split(":");
494
- if (parts.length < 2 || parts.length > 3 || !parts.every((part) => TWO_DIGIT_RE.test(part))) return null;
495
- const minutes = clockToMinutes(Number(parts[0]), Number(parts[1]));
496
- if (minutes === null) return null;
497
- return {
498
- ymd,
499
- minutes
500
- };
501
- }
502
- /** Civil date from either a `YYYY-MM-DD` or a `YYYY-MM-DDTHH:MM` value, so the
503
- * month grid buckets date-only and datetime anchors alike. */
504
- function dateOf(value) {
505
- return parseIsoDate(value) ?? parseIsoDateTime(value)?.ymd ?? null;
506
- }
507
- /** Minutes-of-day from a datetime value, or null for date-only / invalid. */
508
- function timeOf(value) {
509
- return parseIsoDateTime(value)?.minutes ?? null;
510
- }
511
- /** Parse a free-form time-string field into start/end minutes-of-day.
512
- * Handles the common shapes in user data:
513
- * "14:00-17:00" → { start: 840, end: 1020 } (range → block)
514
- * "17:00-" → { start: 1020, end: null } (open end → single line)
515
- * "16:30" → { start: 990, end: null } (point in time → single line)
516
- * "終日" / "" → null (no clock → all-day)
517
- * Returns null when no clock token is parseable. */
518
- function parseTimeRange(value) {
519
- if (typeof value !== "string") return null;
520
- const text = value.trim();
521
- if (!text) return null;
522
- const tokens = [...text.matchAll(CLOCK_RE)];
523
- if (tokens.length === 0) return null;
524
- const minutesOf = (match) => clockToMinutes(Number(match[1]), Number(match[2]));
525
- if (!RANGE_SEP_RE.test(text)) {
526
- const startMin = minutesOf(tokens[0]);
527
- return startMin === null ? null : {
528
- startMin,
529
- endMin: null
530
- };
531
- }
532
- const sepIndex = text.search(RANGE_SEP_RE);
533
- let startMin = null;
534
- let endMin = null;
535
- for (const token of tokens) if ((token.index ?? 0) < sepIndex) startMin = minutesOf(token);
536
- else endMin = minutesOf(token);
537
- if (startMin === null) return null;
538
- return {
539
- startMin,
540
- endMin
541
- };
542
- }
543
- function ymdToUtcMs(ymd) {
544
- return Date.UTC(ymd.year, ymd.month - 1, ymd.day);
545
- }
546
- function utcMsToYmd(epochMs) {
547
- const date = new Date(epochMs);
548
- return {
549
- year: date.getUTCFullYear(),
550
- month: date.getUTCMonth() + 1,
551
- day: date.getUTCDate()
552
- };
553
- }
554
- /** Chronological comparison: negative if `left` precedes `right`, 0 if the
555
- * same day, positive if after. */
556
- function compareYmd(left, right) {
557
- return ymdToUtcMs(left) - ymdToUtcMs(right);
558
- }
559
- /** True iff `day` falls within the inclusive span `[span.start, span.end]`. */
560
- function spanCoversDay(span, day) {
561
- return compareYmd(span.start, day) <= 0 && compareYmd(day, span.end) <= 0;
562
- }
563
- /** Build the 6×7 (42-cell) grid for the given month, including the
564
- * leading/trailing days of the adjacent months so every week is full.
565
- * `month` is 1-12. `weekStartsOn` is 0 (Sunday) … 6 (Saturday). */
566
- function buildMonthGrid(year, month, weekStartsOn = 0) {
567
- const lead = (new Date(Date.UTC(year, month - 1, 1)).getUTCDay() - weekStartsOn + 7) % 7;
568
- const startMs = Date.UTC(year, month - 1, 1) - lead * MS_PER_DAY;
569
- const cells = [];
570
- for (let i = 0; i < 42; i++) {
571
- const ymd = utcMsToYmd(startMs + i * MS_PER_DAY);
572
- cells.push({
573
- ymd,
574
- inMonth: ymd.year === year && ymd.month === month,
575
- key: ymdKey(ymd)
576
- });
577
- }
578
- return cells;
579
- }
580
- /** Resolve a record's calendar span from its date/datetime fields. Returns
581
- * null when the anchor date is missing/invalid (→ the caller's "no date"
582
- * tray). An end date that is missing, invalid, or earlier than the start
583
- * collapses to a single-day span — never an inverted range.
584
- *
585
- * Times for the day (time-allocation) view come from, in priority order:
586
- * 1. the clock on a `datetime` anchor/end value, else
587
- * 2. `timeField` — a separate free-form time-string column (e.g. "14:00-17:00").
588
- * A record with no resolvable clock has `startMin === endMin === null`. */
589
- function recordSpan(item, anchorField, endField, timeField) {
590
- const startRaw = item[anchorField];
591
- const start = dateOf(startRaw);
592
- if (!start) return null;
593
- let end = start;
594
- let startMin = timeOf(startRaw);
595
- let endMin = null;
596
- if (endField) {
597
- const endRaw = item[endField];
598
- const parsedEnd = dateOf(endRaw);
599
- if (parsedEnd && compareYmd(parsedEnd, start) >= 0) {
600
- end = parsedEnd;
601
- endMin = timeOf(endRaw);
602
- }
603
- }
604
- if (timeField && startMin === null && endMin === null) {
605
- const range = parseTimeRange(item[timeField]);
606
- if (range) ({startMin, endMin} = range);
607
- }
608
- return {
609
- item,
610
- start,
611
- end,
612
- startMin,
613
- endMin
614
- };
615
- }
616
- /** Split records into those that land on the calendar (with their spans)
617
- * and those with no usable anchor date (the "no date" tray). Spans are
618
- * sorted by start day so same-day stacking is stable across renders. */
619
- function bucketRecords(items, anchorField, endField, timeField) {
620
- const spans = [];
621
- const noDate = [];
622
- for (const item of items) {
623
- const span = recordSpan(item, anchorField, endField, timeField);
624
- if (span) spans.push(span);
625
- else noDate.push(item);
626
- }
627
- spans.sort((left, right) => compareYmd(left.start, right.start));
628
- return {
629
- spans,
630
- noDate
631
- };
632
- }
633
- /** Project a record's span onto a single day for the time-allocation view, or
634
- * null when the span doesn't cover that day. */
635
- function daySlice(span, day) {
636
- if (!spanCoversDay(span, day)) return null;
637
- const hasStart = span.startMin !== null;
638
- const hasEnd = span.endMin !== null;
639
- if (!hasStart && !hasEnd) return {
640
- kind: "allDay",
641
- startMin: 0,
642
- endMin: MINUTES_PER_DAY,
643
- bleedsBefore: false,
644
- bleedsAfter: false
645
- };
646
- const singleDay = compareYmd(span.start, span.end) === 0;
647
- const isStartDay = compareYmd(day, span.start) === 0;
648
- const isEndDay = compareYmd(day, span.end) === 0;
649
- if (singleDay && hasStart && !hasEnd) return {
650
- kind: "line",
651
- startMin: span.startMin,
652
- endMin: span.startMin,
653
- bleedsBefore: false,
654
- bleedsAfter: false
655
- };
656
- const startMin = isStartDay && hasStart ? span.startMin : 0;
657
- const endMin = isEndDay && hasEnd ? span.endMin : MINUTES_PER_DAY;
658
- if (singleDay && endMin <= startMin) return {
659
- kind: "line",
660
- startMin,
661
- endMin: startMin,
662
- bleedsBefore: false,
663
- bleedsAfter: false
664
- };
665
- return {
666
- kind: "block",
667
- startMin,
668
- endMin,
669
- bleedsBefore: !isStartDay,
670
- bleedsAfter: !isEndDay
671
- };
672
- }
673
- function assignLanes(blocks) {
674
- const order = [...blocks.keys()].sort((left, right) => blocks[left].startMin - blocks[right].startMin || blocks[left].endMin - blocks[right].endMin);
675
- const result = blocks.map(() => ({
676
- lane: 0,
677
- lanes: 1
678
- }));
679
- let cluster = [];
680
- let clusterEnd = Number.NEGATIVE_INFINITY;
681
- const laneEnds = [];
682
- const flush = () => {
683
- for (const index of cluster) result[index].lanes = laneEnds.length;
684
- cluster = [];
685
- laneEnds.length = 0;
686
- clusterEnd = Number.NEGATIVE_INFINITY;
687
- };
688
- for (const index of order) {
689
- const block = blocks[index];
690
- if (cluster.length > 0 && block.startMin >= clusterEnd) flush();
691
- let lane = laneEnds.findIndex((end) => end <= block.startMin);
692
- if (lane === -1) {
693
- lane = laneEnds.length;
694
- laneEnds.push(block.endMin);
695
- } else laneEnds[lane] = block.endMin;
696
- result[index].lane = lane;
697
- cluster.push(index);
698
- clusterEnd = Math.max(clusterEnd, block.endMin);
699
- }
700
- flush();
701
- return result;
702
- }
703
- /** Month label key inputs — returns the 1st of the month as a `Date` so the
704
- * component can feed it to `Intl.DateTimeFormat(locale, …)` for a localized
705
- * "April 2026" header without this module taking a locale dependency. */
706
- function monthAnchorDate(year, month) {
707
- return new Date(Date.UTC(year, month - 1, 1));
708
- }
709
- //#endregion
710
- //#region src/core/errorMessage.ts
711
- /** Normalise an unknown thrown value to a display string. The view layer's host
712
- * capabilities already return normalised `{ ok: false, error }` results, so this
713
- * only backs the defensive `catch` around an unexpected throw. */
714
- function errorMessage(err) {
715
- return err instanceof Error ? err.message : String(err);
716
- }
717
- //#endregion
718
- //#region src/core/shortHexId.ts
719
- /**
720
- * 8-char hex id — short, slug-safe, and editable. Produces the same id *shape*
721
- * as the server's `generateItemId()` (8 hex chars) so a UI-created collection
722
- * record looks like one the server would have generated for a form submitted
723
- * with a blank primary key. The source of randomness differs (UUID-derived here
724
- * vs `randomBytes` on the server); only the shape is intentionally shared.
725
- */
726
- function shortHexId() {
727
- return crypto.randomUUID().replace(/-/g, "").slice(0, 8);
728
- }
729
- //#endregion
730
- //#region src/core/promptSafety.ts
731
- /** Max chars kept — the first batch of a validation issue is enough to act on. */
732
- var DEFANG_MAX_LEN = 200;
733
- function defangForPrompt(value) {
734
- return value.replace(/[<>]/g, "").replace(/`/g, "'").replace(/\$\{/g, "$ {").replace(/\s+/g, " ").slice(0, DEFANG_MAX_LEN);
735
- }
736
- //#endregion
737
- Object.defineProperty(exports, "ENUM_ALERT", {
738
- enumerable: true,
739
- get: function() {
740
- return ENUM_ALERT;
741
- }
742
- });
743
- Object.defineProperty(exports, "ENUM_NEUTRAL", {
744
- enumerable: true,
745
- get: function() {
746
- return ENUM_NEUTRAL;
747
- }
748
- });
749
- Object.defineProperty(exports, "ENUM_NUDGE", {
750
- enumerable: true,
751
- get: function() {
752
- return ENUM_NUDGE;
753
- }
754
- });
755
- Object.defineProperty(exports, "MINUTES_PER_DAY", {
756
- enumerable: true,
757
- get: function() {
758
- return MINUTES_PER_DAY;
759
- }
760
- });
761
- Object.defineProperty(exports, "TOOL_DEFINITION", {
762
- enumerable: true,
763
- get: function() {
764
- return TOOL_DEFINITION;
765
- }
766
- });
767
- Object.defineProperty(exports, "TOOL_NAME", {
768
- enumerable: true,
769
- get: function() {
770
- return TOOL_NAME;
771
- }
772
- });
773
- Object.defineProperty(exports, "actionVisible", {
774
- enumerable: true,
775
- get: function() {
776
- return actionVisible;
777
- }
778
- });
779
- Object.defineProperty(exports, "assignLanes", {
780
- enumerable: true,
781
- get: function() {
782
- return assignLanes;
783
- }
784
- });
785
- Object.defineProperty(exports, "boolSortValue", {
786
- enumerable: true,
787
- get: function() {
788
- return boolSortValue;
789
- }
790
- });
791
- Object.defineProperty(exports, "bucketRecords", {
792
- enumerable: true,
793
- get: function() {
794
- return bucketRecords;
795
- }
796
- });
797
- Object.defineProperty(exports, "buildMonthGrid", {
798
- enumerable: true,
799
- get: function() {
800
- return buildMonthGrid;
801
- }
802
- });
803
- Object.defineProperty(exports, "buildUpdatedRecord", {
804
- enumerable: true,
805
- get: function() {
806
- return buildUpdatedRecord;
807
- }
808
- });
809
- Object.defineProperty(exports, "coerceInlineValue", {
810
- enumerable: true,
811
- get: function() {
812
- return coerceInlineValue;
813
- }
814
- });
815
- Object.defineProperty(exports, "compareSortValues", {
816
- enumerable: true,
817
- get: function() {
818
- return compareSortValues;
819
- }
820
- });
821
- Object.defineProperty(exports, "compareYmd", {
822
- enumerable: true,
823
- get: function() {
824
- return compareYmd;
825
- }
826
- });
827
- Object.defineProperty(exports, "dateOf", {
828
- enumerable: true,
829
- get: function() {
830
- return dateOf;
831
- }
832
- });
833
- Object.defineProperty(exports, "dateSortValue", {
834
- enumerable: true,
835
- get: function() {
836
- return dateSortValue;
837
- }
838
- });
839
- Object.defineProperty(exports, "daySlice", {
840
- enumerable: true,
841
- get: function() {
842
- return daySlice;
843
- }
844
- });
845
- Object.defineProperty(exports, "defangForPrompt", {
846
- enumerable: true,
847
- get: function() {
848
- return defangForPrompt;
849
- }
850
- });
851
- Object.defineProperty(exports, "draftToRecord", {
852
- enumerable: true,
853
- get: function() {
854
- return draftToRecord;
855
- }
856
- });
857
- Object.defineProperty(exports, "emptyRow", {
858
- enumerable: true,
859
- get: function() {
860
- return emptyRow;
861
- }
862
- });
863
- Object.defineProperty(exports, "enumColorClasses", {
864
- enumerable: true,
865
- get: function() {
866
- return enumColorClasses;
867
- }
868
- });
869
- Object.defineProperty(exports, "enumSortValue", {
870
- enumerable: true,
871
- get: function() {
872
- return enumSortValue;
873
- }
874
- });
875
- Object.defineProperty(exports, "enumValueIndex", {
876
- enumerable: true,
877
- get: function() {
878
- return enumValueIndex;
879
- }
880
- });
881
- Object.defineProperty(exports, "errorMessage", {
882
- enumerable: true,
883
- get: function() {
884
- return errorMessage;
885
- }
886
- });
887
- Object.defineProperty(exports, "executePresentCollection", {
888
- enumerable: true,
889
- get: function() {
890
- return executePresentCollection;
891
- }
892
- });
893
- Object.defineProperty(exports, "fieldVisible", {
894
- enumerable: true,
895
- get: function() {
896
- return fieldVisible;
897
- }
898
- });
899
- Object.defineProperty(exports, "firstMissingRequiredField", {
900
- enumerable: true,
901
- get: function() {
902
- return firstMissingRequiredField;
903
- }
904
- });
905
- Object.defineProperty(exports, "isSortableField", {
906
- enumerable: true,
907
- get: function() {
908
- return isSortableField;
909
- }
910
- });
911
- Object.defineProperty(exports, "itemIdOf", {
912
- enumerable: true,
913
- get: function() {
914
- return itemIdOf;
915
- }
916
- });
917
- Object.defineProperty(exports, "itemLabelOf", {
918
- enumerable: true,
919
- get: function() {
920
- return itemLabelOf;
921
- }
922
- });
923
- Object.defineProperty(exports, "labelFieldFor", {
924
- enumerable: true,
925
- get: function() {
926
- return labelFieldFor;
927
- }
928
- });
929
- Object.defineProperty(exports, "monthAnchorDate", {
930
- enumerable: true,
931
- get: function() {
932
- return monthAnchorDate;
933
- }
934
- });
935
- Object.defineProperty(exports, "nextSortDirection", {
936
- enumerable: true,
937
- get: function() {
938
- return nextSortDirection;
939
- }
940
- });
941
- Object.defineProperty(exports, "numericSortValue", {
942
- enumerable: true,
943
- get: function() {
944
- return numericSortValue;
945
- }
946
- });
947
- Object.defineProperty(exports, "parseIsoDate", {
948
- enumerable: true,
949
- get: function() {
950
- return parseIsoDate;
951
- }
952
- });
953
- Object.defineProperty(exports, "parseIsoDateTime", {
954
- enumerable: true,
955
- get: function() {
956
- return parseIsoDateTime;
957
- }
958
- });
959
- Object.defineProperty(exports, "parseTimeRange", {
960
- enumerable: true,
961
- get: function() {
962
- return parseTimeRange;
963
- }
964
- });
965
- Object.defineProperty(exports, "recordSpan", {
966
- enumerable: true,
967
- get: function() {
968
- return recordSpan;
969
- }
970
- });
971
- Object.defineProperty(exports, "resolveEnumColor", {
972
- enumerable: true,
973
- get: function() {
974
- return resolveEnumColor;
975
- }
976
- });
977
- Object.defineProperty(exports, "rowFromItem", {
978
- enumerable: true,
979
- get: function() {
980
- return rowFromItem;
981
- }
982
- });
983
- Object.defineProperty(exports, "shortHexId", {
984
- enumerable: true,
985
- get: function() {
986
- return shortHexId;
987
- }
988
- });
989
- Object.defineProperty(exports, "sortItems", {
990
- enumerable: true,
991
- get: function() {
992
- return sortItems;
993
- }
994
- });
995
- Object.defineProperty(exports, "spanCoversDay", {
996
- enumerable: true,
997
- get: function() {
998
- return spanCoversDay;
999
- }
1000
- });
1001
- Object.defineProperty(exports, "stringSortValue", {
1002
- enumerable: true,
1003
- get: function() {
1004
- return stringSortValue;
1005
- }
1006
- });
1007
- Object.defineProperty(exports, "whenMatches", {
1008
- enumerable: true,
1009
- get: function() {
1010
- return whenMatches;
1011
- }
1012
- });
1013
- Object.defineProperty(exports, "ymdKey", {
1014
- enumerable: true,
1015
- get: function() {
1016
- return ymdKey;
1017
- }
1018
- });
1019
-
1020
- //# sourceMappingURL=promptSafety-B0pPJkfQ.cjs.map