@mulmoclaude/core 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/helps/billing-clients-worklog.md +15 -12
- package/assets/helps/collection-skills.md +42 -19
- package/assets/helps/gemini.md +5 -3
- package/assets/helps/todo-collection.md +13 -7
- package/dist/{deriveAll-CMFXDQ_G.js → calendarGrid-C1rfCXJ3.cjs} +468 -3
- package/dist/calendarGrid-C1rfCXJ3.cjs.map +1 -0
- package/dist/{deriveAll-BQ_p5HSB.cjs → calendarGrid-kO6rGfm0.js} +289 -74
- package/dist/calendarGrid-kO6rGfm0.js.map +1 -0
- package/dist/collection/core/ids.d.ts +12 -0
- package/dist/collection/core/recordZ.d.ts +25 -0
- package/dist/collection/core/schema.d.ts +65 -418
- package/dist/collection/core/schemaZ.d.ts +846 -0
- package/dist/collection/core/templatePath.d.ts +34 -0
- package/dist/collection/core/where.d.ts +9 -15
- package/dist/collection/index.cjs +34 -295
- package/dist/collection/index.cjs.map +1 -1
- package/dist/collection/index.d.ts +1 -0
- package/dist/collection/index.js +5 -270
- package/dist/collection/index.js.map +1 -1
- package/dist/collection/paths.cjs +1 -1
- package/dist/collection/paths.cjs.map +1 -1
- package/dist/collection/paths.js +1 -1
- package/dist/collection/paths.js.map +1 -1
- package/dist/collection/registry/server/index.cjs +1 -1
- package/dist/collection/registry/server/index.js +1 -1
- package/dist/collection/server/discovery.d.ts +2 -244
- package/dist/collection/server/index.cjs +3 -1
- package/dist/collection/server/index.js +2 -2
- package/dist/collection/server/templatePath.d.ts +1 -34
- package/dist/collection/server/validate.d.ts +11 -7
- package/dist/collection-watchers/clock.d.ts +1 -0
- package/dist/collection-watchers/index.cjs +13 -4
- package/dist/collection-watchers/index.cjs.map +1 -1
- package/dist/collection-watchers/index.d.ts +1 -0
- package/dist/collection-watchers/index.js +13 -5
- package/dist/collection-watchers/index.js.map +1 -1
- package/dist/feeds/index.cjs +4 -4
- package/dist/feeds/index.js +2 -2
- package/dist/feeds/ingestTypes.d.ts +15 -40
- package/dist/feeds/server/index.cjs +5 -5
- package/dist/feeds/server/index.js +3 -3
- package/dist/{ingestTypes-C7EheYZX.cjs → ingestTypes-BIFXlw7M.cjs} +3 -3
- package/dist/ingestTypes-BIFXlw7M.cjs.map +1 -0
- package/dist/{ingestTypes-CmJeOUJc.js → ingestTypes-KuYPX9Ea.js} +2 -2
- package/dist/ingestTypes-KuYPX9Ea.js.map +1 -0
- package/dist/{server-DKXXFTbw.js → server-Cc6XcyOA.js} +417 -187
- package/dist/server-Cc6XcyOA.js.map +1 -0
- package/dist/{server-G6GtOdaW.cjs → server-Cd5WdM8a.cjs} +440 -198
- package/dist/server-Cd5WdM8a.cjs.map +1 -0
- package/package.json +2 -2
- package/dist/deriveAll-BQ_p5HSB.cjs.map +0 -1
- package/dist/deriveAll-CMFXDQ_G.js.map +0 -1
- package/dist/ingestTypes-C7EheYZX.cjs.map +0 -1
- package/dist/ingestTypes-CmJeOUJc.js.map +0 -1
- package/dist/server-DKXXFTbw.js.map +0 -1
- package/dist/server-G6GtOdaW.cjs.map +0 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A safe skill-relative path: non-empty, no backslash, not absolute,
|
|
3
|
+
* and every `/`-separated segment is a plain `[A-Za-z0-9._-]+` token
|
|
4
|
+
* that isn't `.` / `..` (no traversal). Multi-segment (nested) paths
|
|
5
|
+
* are allowed. The reader's realpath containment is the hard
|
|
6
|
+
* guarantee; this fails a bad path fast.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isSafeTemplatePath(value: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* An action `template` value: a safe path that lives under the skill's
|
|
11
|
+
* `templates/` subdir. This is the canonical contract — the schema
|
|
12
|
+
* validator rejects anything else up front, and the bridge mirrors
|
|
13
|
+
* exactly these. Nested paths (`templates/mail/welcome.md`) and any
|
|
14
|
+
* extension are allowed as long as the path is otherwise safe.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isSafeActionTemplatePath(value: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* A custom-view `file` value: a safe path under the skill's `views/`
|
|
19
|
+
* subdir that ends in `.html`. Custom views are LLM-authored HTML the
|
|
20
|
+
* host renders in a sandboxed iframe; constraining them to `views/*.html`
|
|
21
|
+
* keeps the data folder and the schema/template files off-limits to the
|
|
22
|
+
* view-file reader. Same per-segment safety as templates; the reader's
|
|
23
|
+
* realpath containment is the hard guarantee.
|
|
24
|
+
*/
|
|
25
|
+
export declare function isSafeCustomViewPath(value: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* A custom-view `i18n` value: the JSON dictionary file authors ship next to
|
|
28
|
+
* their `views/<id>.html`. Constrained to `views/<name>.i18n.json` so the
|
|
29
|
+
* field can only point at a translation file co-located with the view it
|
|
30
|
+
* translates — the same `views/` containment the HTML reader uses. The
|
|
31
|
+
* `.i18n.json` suffix (vs plain `.json`) makes the file's purpose grep-able
|
|
32
|
+
* for both authors and tooling.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isSafeCustomViewI18nPath(value: string): boolean;
|
|
@@ -1,28 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ValueRefZ, WhereCondZ, WhereZ } from './schemaZ';
|
|
3
3
|
/** Reads the comparison value from a field instead of a schema literal:
|
|
4
4
|
* - `record` set → another record: `recordsById[record][field]` (e.g. a
|
|
5
5
|
* `_config` singleton's `defaultCity`, following a per-user setting);
|
|
6
6
|
* - `record` omitted → the SAME record being matched (field-to-field, e.g.
|
|
7
7
|
* `spent > budget`). */
|
|
8
|
-
export
|
|
9
|
-
record?: string;
|
|
10
|
-
field: string;
|
|
11
|
-
}
|
|
8
|
+
export type ValueRef = z.infer<typeof ValueRefZ>;
|
|
12
9
|
/** One typed condition: `record[field] <op> value`. The comparison value is
|
|
13
10
|
* either a literal `value` (a plain string for every op except `in`, which
|
|
14
11
|
* takes the allowed set) or a `valueFrom` reference resolved against the
|
|
15
12
|
* `recordsById` map passed to `matchesWhere`. Exactly one of the two is
|
|
16
|
-
* expected — enforced by zod at the schema boundary (`
|
|
17
|
-
*
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
value?: string | string[];
|
|
22
|
-
valueFrom?: ValueRef;
|
|
23
|
-
}
|
|
13
|
+
* expected — enforced by zod at the schema boundary (`./schemaZ`), not
|
|
14
|
+
* here. */
|
|
15
|
+
export type WhereCond = z.infer<typeof WhereCondZ>;
|
|
16
|
+
/** Comparison operators one `WhereCond` may apply to `record[field]`. */
|
|
17
|
+
export type WhereOp = WhereCond["op"];
|
|
24
18
|
/** A `where` clause is the AND of its conditions — every one must match. */
|
|
25
|
-
export type Where =
|
|
19
|
+
export type Where = z.infer<typeof WhereZ>;
|
|
26
20
|
/** True when `record` satisfies every condition in `where` (AND). An empty
|
|
27
21
|
* `where` matches everything. `recordsById` — the source collection's
|
|
28
22
|
* records keyed by primaryKey — resolves any `valueFrom` reference;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
2
|
+
const require_calendarGrid = require("../calendarGrid-C1rfCXJ3.cjs");
|
|
3
3
|
const require_errorMessage = require("../errorMessage--LvXe_Jx.cjs");
|
|
4
4
|
//#region src/collection/core/presentCollection.ts
|
|
5
5
|
var TOOL_NAME = "presentCollection";
|
|
@@ -149,7 +149,8 @@ function resolveEnumColor(schema, fieldKey, value) {
|
|
|
149
149
|
if (rank < 0) return ENUM_NEUTRAL;
|
|
150
150
|
return rank === 0 ? ENUM_ALERT : ENUM_NUDGE;
|
|
151
151
|
}
|
|
152
|
-
|
|
152
|
+
const spec = schema.fields[fieldKey];
|
|
153
|
+
return enumColorClasses(enumValueIndex(spec?.type === "enum" ? spec.values : void 0, value));
|
|
153
154
|
}
|
|
154
155
|
//#endregion
|
|
155
156
|
//#region src/collection/core/actionVisible.ts
|
|
@@ -294,7 +295,7 @@ function isMissingDraftValue(value) {
|
|
|
294
295
|
}
|
|
295
296
|
/** Label of the first required table sub-field empty in any row, else null. */
|
|
296
297
|
function firstMissingTableSubField(field, rows) {
|
|
297
|
-
if (
|
|
298
|
+
if (field.type !== "table" || !rows) return null;
|
|
298
299
|
for (let rowIdx = 0; rowIdx < rows.length; rowIdx++) {
|
|
299
300
|
const row = rows[rowIdx];
|
|
300
301
|
for (const [subKey, subField] of Object.entries(field.of)) {
|
|
@@ -444,272 +445,6 @@ function itemLabelOf(item, schema, labelField) {
|
|
|
444
445
|
return itemIdOf(item, schema);
|
|
445
446
|
}
|
|
446
447
|
//#endregion
|
|
447
|
-
//#region src/collection/core/calendarGrid.ts
|
|
448
|
-
var MS_PER_DAY = 864e5;
|
|
449
|
-
var ISO_DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
450
|
-
var TWO_DIGIT_RE = /^\d{2}$/;
|
|
451
|
-
var CLOCK_RE = /(\d{1,2}):(\d{2})/g;
|
|
452
|
-
var RANGE_SEP_RE = /[-–—~〜~]/;
|
|
453
|
-
/** Minutes in a full day — the timeline's vertical extent. */
|
|
454
|
-
var MINUTES_PER_DAY = 1440;
|
|
455
|
-
function pad2(value) {
|
|
456
|
-
return String(value).padStart(2, "0");
|
|
457
|
-
}
|
|
458
|
-
/** Canonical `YYYY-MM-DD` string for a civil date. */
|
|
459
|
-
function ymdKey(ymd) {
|
|
460
|
-
return `${String(ymd.year).padStart(4, "0")}-${pad2(ymd.month)}-${pad2(ymd.day)}`;
|
|
461
|
-
}
|
|
462
|
-
/** Strictly parse a `YYYY-MM-DD` string into a civil date, rejecting
|
|
463
|
-
* anything that isn't a real calendar day (e.g. `2026-02-30`, `2026-13-01`).
|
|
464
|
-
* Returns null for non-strings and malformed values so callers can route
|
|
465
|
-
* records with no usable date into the "no date" tray rather than crash. */
|
|
466
|
-
function parseIsoDate(value) {
|
|
467
|
-
if (typeof value !== "string") return null;
|
|
468
|
-
const match = ISO_DATE_RE.exec(value.trim());
|
|
469
|
-
if (!match) return null;
|
|
470
|
-
const year = Number(match[1]);
|
|
471
|
-
const month = Number(match[2]);
|
|
472
|
-
const day = Number(match[3]);
|
|
473
|
-
const probe = new Date(Date.UTC(year, month - 1, day));
|
|
474
|
-
if (probe.getUTCFullYear() !== year || probe.getUTCMonth() !== month - 1 || probe.getUTCDate() !== day) return null;
|
|
475
|
-
return {
|
|
476
|
-
year,
|
|
477
|
-
month,
|
|
478
|
-
day
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
/** Minutes-of-day for an `HH:MM` pair, or null when out of range. */
|
|
482
|
-
function clockToMinutes(hours, minutes) {
|
|
483
|
-
if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null;
|
|
484
|
-
return hours * 60 + minutes;
|
|
485
|
-
}
|
|
486
|
-
/** Strictly parse a `YYYY-MM-DDTHH:MM` (optional `:SS`) datetime into its
|
|
487
|
-
* civil date and minutes-of-day. Returns null for anything that isn't a real
|
|
488
|
-
* calendar day or a valid 24h clock. */
|
|
489
|
-
function parseIsoDateTime(value) {
|
|
490
|
-
if (typeof value !== "string") return null;
|
|
491
|
-
const trimmed = value.trim();
|
|
492
|
-
const tIndex = trimmed.indexOf("T");
|
|
493
|
-
if (tIndex === -1) return null;
|
|
494
|
-
const ymd = parseIsoDate(trimmed.slice(0, tIndex));
|
|
495
|
-
if (!ymd) return null;
|
|
496
|
-
const parts = trimmed.slice(tIndex + 1).split(":");
|
|
497
|
-
if (parts.length < 2 || parts.length > 3 || !parts.every((part) => TWO_DIGIT_RE.test(part))) return null;
|
|
498
|
-
const minutes = clockToMinutes(Number(parts[0]), Number(parts[1]));
|
|
499
|
-
if (minutes === null) return null;
|
|
500
|
-
return {
|
|
501
|
-
ymd,
|
|
502
|
-
minutes
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
/** Civil date from either a `YYYY-MM-DD` or a `YYYY-MM-DDTHH:MM` value, so the
|
|
506
|
-
* month grid buckets date-only and datetime anchors alike. */
|
|
507
|
-
function dateOf(value) {
|
|
508
|
-
return parseIsoDate(value) ?? parseIsoDateTime(value)?.ymd ?? null;
|
|
509
|
-
}
|
|
510
|
-
/** Minutes-of-day from a datetime value, or null for date-only / invalid. */
|
|
511
|
-
function timeOf(value) {
|
|
512
|
-
return parseIsoDateTime(value)?.minutes ?? null;
|
|
513
|
-
}
|
|
514
|
-
/** Parse a free-form time-string field into start/end minutes-of-day.
|
|
515
|
-
* Handles the common shapes in user data:
|
|
516
|
-
* "14:00-17:00" → { start: 840, end: 1020 } (range → block)
|
|
517
|
-
* "17:00-" → { start: 1020, end: null } (open end → single line)
|
|
518
|
-
* "16:30" → { start: 990, end: null } (point in time → single line)
|
|
519
|
-
* "終日" / "" → null (no clock → all-day)
|
|
520
|
-
* Returns null when no clock token is parseable. */
|
|
521
|
-
function parseTimeRange(value) {
|
|
522
|
-
if (typeof value !== "string") return null;
|
|
523
|
-
const text = value.trim();
|
|
524
|
-
if (!text) return null;
|
|
525
|
-
const tokens = [...text.matchAll(CLOCK_RE)];
|
|
526
|
-
if (tokens.length === 0) return null;
|
|
527
|
-
const minutesOf = (match) => clockToMinutes(Number(match[1]), Number(match[2]));
|
|
528
|
-
if (!RANGE_SEP_RE.test(text)) {
|
|
529
|
-
const startMin = minutesOf(tokens[0]);
|
|
530
|
-
return startMin === null ? null : {
|
|
531
|
-
startMin,
|
|
532
|
-
endMin: null
|
|
533
|
-
};
|
|
534
|
-
}
|
|
535
|
-
const sepIndex = text.search(RANGE_SEP_RE);
|
|
536
|
-
let startMin = null;
|
|
537
|
-
let endMin = null;
|
|
538
|
-
for (const token of tokens) if ((token.index ?? 0) < sepIndex) startMin = minutesOf(token);
|
|
539
|
-
else endMin = minutesOf(token);
|
|
540
|
-
if (startMin === null) return null;
|
|
541
|
-
return {
|
|
542
|
-
startMin,
|
|
543
|
-
endMin
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
function ymdToUtcMs(ymd) {
|
|
547
|
-
return Date.UTC(ymd.year, ymd.month - 1, ymd.day);
|
|
548
|
-
}
|
|
549
|
-
function utcMsToYmd(epochMs) {
|
|
550
|
-
const date = new Date(epochMs);
|
|
551
|
-
return {
|
|
552
|
-
year: date.getUTCFullYear(),
|
|
553
|
-
month: date.getUTCMonth() + 1,
|
|
554
|
-
day: date.getUTCDate()
|
|
555
|
-
};
|
|
556
|
-
}
|
|
557
|
-
/** Chronological comparison: negative if `left` precedes `right`, 0 if the
|
|
558
|
-
* same day, positive if after. */
|
|
559
|
-
function compareYmd(left, right) {
|
|
560
|
-
return ymdToUtcMs(left) - ymdToUtcMs(right);
|
|
561
|
-
}
|
|
562
|
-
/** True iff `day` falls within the inclusive span `[span.start, span.end]`. */
|
|
563
|
-
function spanCoversDay(span, day) {
|
|
564
|
-
return compareYmd(span.start, day) <= 0 && compareYmd(day, span.end) <= 0;
|
|
565
|
-
}
|
|
566
|
-
/** Build the 6×7 (42-cell) grid for the given month, including the
|
|
567
|
-
* leading/trailing days of the adjacent months so every week is full.
|
|
568
|
-
* `month` is 1-12. `weekStartsOn` is 0 (Sunday) … 6 (Saturday). */
|
|
569
|
-
function buildMonthGrid(year, month, weekStartsOn = 0) {
|
|
570
|
-
const lead = (new Date(Date.UTC(year, month - 1, 1)).getUTCDay() - weekStartsOn + 7) % 7;
|
|
571
|
-
const startMs = Date.UTC(year, month - 1, 1) - lead * MS_PER_DAY;
|
|
572
|
-
const cells = [];
|
|
573
|
-
for (let i = 0; i < 42; i++) {
|
|
574
|
-
const ymd = utcMsToYmd(startMs + i * MS_PER_DAY);
|
|
575
|
-
cells.push({
|
|
576
|
-
ymd,
|
|
577
|
-
inMonth: ymd.year === year && ymd.month === month,
|
|
578
|
-
key: ymdKey(ymd)
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
return cells;
|
|
582
|
-
}
|
|
583
|
-
/** Resolve a record's calendar span from its date/datetime fields. Returns
|
|
584
|
-
* null when the anchor date is missing/invalid (→ the caller's "no date"
|
|
585
|
-
* tray). An end date that is missing, invalid, or earlier than the start
|
|
586
|
-
* collapses to a single-day span — never an inverted range.
|
|
587
|
-
*
|
|
588
|
-
* Times for the day (time-allocation) view come from, in priority order:
|
|
589
|
-
* 1. the clock on a `datetime` anchor/end value, else
|
|
590
|
-
* 2. `timeField` — a separate free-form time-string column (e.g. "14:00-17:00").
|
|
591
|
-
* A record with no resolvable clock has `startMin === endMin === null`. */
|
|
592
|
-
function recordSpan(item, anchorField, endField, timeField) {
|
|
593
|
-
const startRaw = item[anchorField];
|
|
594
|
-
const start = dateOf(startRaw);
|
|
595
|
-
if (!start) return null;
|
|
596
|
-
let end = start;
|
|
597
|
-
let startMin = timeOf(startRaw);
|
|
598
|
-
let endMin = null;
|
|
599
|
-
if (endField) {
|
|
600
|
-
const endRaw = item[endField];
|
|
601
|
-
const parsedEnd = dateOf(endRaw);
|
|
602
|
-
if (parsedEnd && compareYmd(parsedEnd, start) >= 0) {
|
|
603
|
-
end = parsedEnd;
|
|
604
|
-
endMin = timeOf(endRaw);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
if (timeField && startMin === null && endMin === null) {
|
|
608
|
-
const range = parseTimeRange(item[timeField]);
|
|
609
|
-
if (range) ({startMin, endMin} = range);
|
|
610
|
-
}
|
|
611
|
-
return {
|
|
612
|
-
item,
|
|
613
|
-
start,
|
|
614
|
-
end,
|
|
615
|
-
startMin,
|
|
616
|
-
endMin
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
/** Split records into those that land on the calendar (with their spans)
|
|
620
|
-
* and those with no usable anchor date (the "no date" tray). Spans are
|
|
621
|
-
* sorted by start day so same-day stacking is stable across renders. */
|
|
622
|
-
function bucketRecords(items, anchorField, endField, timeField) {
|
|
623
|
-
const spans = [];
|
|
624
|
-
const noDate = [];
|
|
625
|
-
for (const item of items) {
|
|
626
|
-
const span = recordSpan(item, anchorField, endField, timeField);
|
|
627
|
-
if (span) spans.push(span);
|
|
628
|
-
else noDate.push(item);
|
|
629
|
-
}
|
|
630
|
-
spans.sort((left, right) => compareYmd(left.start, right.start));
|
|
631
|
-
return {
|
|
632
|
-
spans,
|
|
633
|
-
noDate
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
/** Project a record's span onto a single day for the time-allocation view, or
|
|
637
|
-
* null when the span doesn't cover that day. */
|
|
638
|
-
function daySlice(span, day) {
|
|
639
|
-
if (!spanCoversDay(span, day)) return null;
|
|
640
|
-
const hasStart = span.startMin !== null;
|
|
641
|
-
const hasEnd = span.endMin !== null;
|
|
642
|
-
if (!hasStart && !hasEnd) return {
|
|
643
|
-
kind: "allDay",
|
|
644
|
-
startMin: 0,
|
|
645
|
-
endMin: MINUTES_PER_DAY,
|
|
646
|
-
bleedsBefore: false,
|
|
647
|
-
bleedsAfter: false
|
|
648
|
-
};
|
|
649
|
-
const singleDay = compareYmd(span.start, span.end) === 0;
|
|
650
|
-
const isStartDay = compareYmd(day, span.start) === 0;
|
|
651
|
-
const isEndDay = compareYmd(day, span.end) === 0;
|
|
652
|
-
if (singleDay && hasStart && !hasEnd) return {
|
|
653
|
-
kind: "line",
|
|
654
|
-
startMin: span.startMin,
|
|
655
|
-
endMin: span.startMin,
|
|
656
|
-
bleedsBefore: false,
|
|
657
|
-
bleedsAfter: false
|
|
658
|
-
};
|
|
659
|
-
const startMin = isStartDay && hasStart ? span.startMin : 0;
|
|
660
|
-
const endMin = isEndDay && hasEnd ? span.endMin : MINUTES_PER_DAY;
|
|
661
|
-
if (singleDay && endMin <= startMin) return {
|
|
662
|
-
kind: "line",
|
|
663
|
-
startMin,
|
|
664
|
-
endMin: startMin,
|
|
665
|
-
bleedsBefore: false,
|
|
666
|
-
bleedsAfter: false
|
|
667
|
-
};
|
|
668
|
-
return {
|
|
669
|
-
kind: "block",
|
|
670
|
-
startMin,
|
|
671
|
-
endMin,
|
|
672
|
-
bleedsBefore: !isStartDay,
|
|
673
|
-
bleedsAfter: !isEndDay
|
|
674
|
-
};
|
|
675
|
-
}
|
|
676
|
-
function assignLanes(blocks) {
|
|
677
|
-
const order = [...blocks.keys()].sort((left, right) => blocks[left].startMin - blocks[right].startMin || blocks[left].endMin - blocks[right].endMin);
|
|
678
|
-
const result = blocks.map(() => ({
|
|
679
|
-
lane: 0,
|
|
680
|
-
lanes: 1
|
|
681
|
-
}));
|
|
682
|
-
let cluster = [];
|
|
683
|
-
let clusterEnd = Number.NEGATIVE_INFINITY;
|
|
684
|
-
const laneEnds = [];
|
|
685
|
-
const flush = () => {
|
|
686
|
-
for (const index of cluster) result[index].lanes = laneEnds.length;
|
|
687
|
-
cluster = [];
|
|
688
|
-
laneEnds.length = 0;
|
|
689
|
-
clusterEnd = Number.NEGATIVE_INFINITY;
|
|
690
|
-
};
|
|
691
|
-
for (const index of order) {
|
|
692
|
-
const block = blocks[index];
|
|
693
|
-
if (cluster.length > 0 && block.startMin >= clusterEnd) flush();
|
|
694
|
-
let lane = laneEnds.findIndex((end) => end <= block.startMin);
|
|
695
|
-
if (lane === -1) {
|
|
696
|
-
lane = laneEnds.length;
|
|
697
|
-
laneEnds.push(block.endMin);
|
|
698
|
-
} else laneEnds[lane] = block.endMin;
|
|
699
|
-
result[index].lane = lane;
|
|
700
|
-
cluster.push(index);
|
|
701
|
-
clusterEnd = Math.max(clusterEnd, block.endMin);
|
|
702
|
-
}
|
|
703
|
-
flush();
|
|
704
|
-
return result;
|
|
705
|
-
}
|
|
706
|
-
/** Month label key inputs — returns the 1st of the month as a `Date` so the
|
|
707
|
-
* component can feed it to `Intl.DateTimeFormat(locale, …)` for a localized
|
|
708
|
-
* "April 2026" header without this module taking a locale dependency. */
|
|
709
|
-
function monthAnchorDate(year, month) {
|
|
710
|
-
return new Date(Date.UTC(year, month - 1, 1));
|
|
711
|
-
}
|
|
712
|
-
//#endregion
|
|
713
448
|
//#region src/collection/core/shortHexId.ts
|
|
714
449
|
/**
|
|
715
450
|
* 8-char hex id — short, slug-safe, and editable. Produces the same id *shape*
|
|
@@ -729,64 +464,68 @@ function defangForPrompt(value) {
|
|
|
729
464
|
return value.replace(/[<>]/g, "").replace(/`/g, "'").replace(/\$\{/g, "$ {").replace(/\s+/g, " ").slice(0, DEFANG_MAX_LEN);
|
|
730
465
|
}
|
|
731
466
|
//#endregion
|
|
732
|
-
exports.AGENT_INGEST_KIND =
|
|
467
|
+
exports.AGENT_INGEST_KIND = require_calendarGrid.AGENT_INGEST_KIND;
|
|
733
468
|
exports.ENUM_ALERT = ENUM_ALERT;
|
|
734
469
|
exports.ENUM_NEUTRAL = ENUM_NEUTRAL;
|
|
735
470
|
exports.ENUM_NUDGE = ENUM_NUDGE;
|
|
736
|
-
exports.FEED_SCHEDULES =
|
|
737
|
-
exports.INGEST_KINDS =
|
|
738
|
-
exports.MINUTES_PER_DAY = MINUTES_PER_DAY;
|
|
471
|
+
exports.FEED_SCHEDULES = require_calendarGrid.FEED_SCHEDULES;
|
|
472
|
+
exports.INGEST_KINDS = require_calendarGrid.INGEST_KINDS;
|
|
473
|
+
exports.MINUTES_PER_DAY = require_calendarGrid.MINUTES_PER_DAY;
|
|
474
|
+
exports.SAFE_RECORD_ID_PATTERN = require_calendarGrid.SAFE_RECORD_ID_PATTERN;
|
|
475
|
+
exports.SAFE_SLUG_PATTERN = require_calendarGrid.SAFE_SLUG_PATTERN;
|
|
739
476
|
exports.TOOL_DEFINITION = TOOL_DEFINITION;
|
|
740
477
|
exports.TOOL_NAME = TOOL_NAME;
|
|
741
478
|
exports.actionVisible = actionVisible;
|
|
742
|
-
exports.assignLanes = assignLanes;
|
|
479
|
+
exports.assignLanes = require_calendarGrid.assignLanes;
|
|
743
480
|
exports.boolSortValue = boolSortValue;
|
|
744
|
-
exports.bucketRecords = bucketRecords;
|
|
745
|
-
exports.buildMonthGrid = buildMonthGrid;
|
|
481
|
+
exports.bucketRecords = require_calendarGrid.bucketRecords;
|
|
482
|
+
exports.buildMonthGrid = require_calendarGrid.buildMonthGrid;
|
|
746
483
|
exports.buildUpdatedRecord = buildUpdatedRecord;
|
|
747
484
|
exports.coerceInlineValue = coerceInlineValue;
|
|
748
485
|
exports.compareSortValues = compareSortValues;
|
|
749
|
-
exports.compareYmd = compareYmd;
|
|
750
|
-
exports.dateOf = dateOf;
|
|
486
|
+
exports.compareYmd = require_calendarGrid.compareYmd;
|
|
487
|
+
exports.dateOf = require_calendarGrid.dateOf;
|
|
751
488
|
exports.dateSortValue = dateSortValue;
|
|
752
|
-
exports.daySlice = daySlice;
|
|
489
|
+
exports.daySlice = require_calendarGrid.daySlice;
|
|
753
490
|
exports.defangForPrompt = defangForPrompt;
|
|
754
|
-
exports.deriveAll =
|
|
491
|
+
exports.deriveAll = require_calendarGrid.deriveAll;
|
|
755
492
|
exports.draftToRecord = draftToRecord;
|
|
756
|
-
exports.embedTargetId =
|
|
493
|
+
exports.embedTargetId = require_calendarGrid.embedTargetId;
|
|
757
494
|
exports.emptyRow = emptyRow;
|
|
758
495
|
exports.enumColorClasses = enumColorClasses;
|
|
759
496
|
exports.enumSortValue = enumSortValue;
|
|
760
497
|
exports.enumValueIndex = enumValueIndex;
|
|
761
498
|
exports.errorMessage = require_errorMessage.errorMessage;
|
|
762
|
-
exports.evaluateDerived =
|
|
499
|
+
exports.evaluateDerived = require_calendarGrid.evaluateDerived;
|
|
763
500
|
exports.executePresentCollection = executePresentCollection;
|
|
764
501
|
exports.fieldVisible = fieldVisible;
|
|
765
|
-
exports.firstDateField =
|
|
502
|
+
exports.firstDateField = require_calendarGrid.firstDateField;
|
|
766
503
|
exports.firstMissingRequiredField = firstMissingRequiredField;
|
|
767
|
-
exports.isFieldDrivenEvery =
|
|
504
|
+
exports.isFieldDrivenEvery = require_calendarGrid.isFieldDrivenEvery;
|
|
505
|
+
exports.isSafeRecordId = require_calendarGrid.isSafeRecordId;
|
|
506
|
+
exports.isSafeSlug = require_calendarGrid.isSafeSlug;
|
|
768
507
|
exports.isSortableField = isSortableField;
|
|
769
508
|
exports.itemIdOf = itemIdOf;
|
|
770
509
|
exports.itemLabelOf = itemLabelOf;
|
|
771
510
|
exports.labelFieldFor = labelFieldFor;
|
|
772
|
-
exports.matchesWhere =
|
|
773
|
-
exports.monthAnchorDate = monthAnchorDate;
|
|
511
|
+
exports.matchesWhere = require_calendarGrid.matchesWhere;
|
|
512
|
+
exports.monthAnchorDate = require_calendarGrid.monthAnchorDate;
|
|
774
513
|
exports.nextSortDirection = nextSortDirection;
|
|
775
514
|
exports.numericSortValue = numericSortValue;
|
|
776
|
-
exports.parseIsoDate = parseIsoDate;
|
|
777
|
-
exports.parseIsoDateTime = parseIsoDateTime;
|
|
778
|
-
exports.parseTimeRange = parseTimeRange;
|
|
779
|
-
exports.recordSpan = recordSpan;
|
|
515
|
+
exports.parseIsoDate = require_calendarGrid.parseIsoDate;
|
|
516
|
+
exports.parseIsoDateTime = require_calendarGrid.parseIsoDateTime;
|
|
517
|
+
exports.parseTimeRange = require_calendarGrid.parseTimeRange;
|
|
518
|
+
exports.recordSpan = require_calendarGrid.recordSpan;
|
|
780
519
|
exports.resolveEnumColor = resolveEnumColor;
|
|
781
|
-
exports.resolveIcon =
|
|
782
|
-
exports.resolveRowRefs =
|
|
520
|
+
exports.resolveIcon = require_calendarGrid.resolveIcon;
|
|
521
|
+
exports.resolveRowRefs = require_calendarGrid.resolveRowRefs;
|
|
783
522
|
exports.rowFromItem = rowFromItem;
|
|
784
|
-
exports.selectDynamicRecord =
|
|
523
|
+
exports.selectDynamicRecord = require_calendarGrid.selectDynamicRecord;
|
|
785
524
|
exports.shortHexId = shortHexId;
|
|
786
525
|
exports.sortItems = sortItems;
|
|
787
|
-
exports.spanCoversDay = spanCoversDay;
|
|
526
|
+
exports.spanCoversDay = require_calendarGrid.spanCoversDay;
|
|
788
527
|
exports.stringSortValue = stringSortValue;
|
|
789
528
|
exports.whenMatches = whenMatches;
|
|
790
|
-
exports.ymdKey = ymdKey;
|
|
529
|
+
exports.ymdKey = require_calendarGrid.ymdKey;
|
|
791
530
|
|
|
792
531
|
//# sourceMappingURL=index.cjs.map
|