@chelone/core 0.6.0 → 0.7.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/dist/assistant/assistant-session.d.ts +6 -0
- package/dist/assistant/assistant-session.js +204 -181
- package/dist/assistant/echo-transport.js +26 -29
- package/dist/assistant/http-transport.d.ts +10 -0
- package/dist/assistant/http-transport.js +98 -0
- package/dist/assistant/types.d.ts +1 -7
- package/dist/auth/access-control.d.ts +13 -5
- package/dist/auth/access-control.js +25 -21
- package/dist/auth/auth-options.d.ts +21 -17
- package/dist/auth/auth-options.js +80 -48
- package/dist/auth/permission-matrix.d.ts +12 -0
- package/dist/auth/permission-matrix.js +67 -0
- package/dist/auth/types.d.ts +14 -4
- package/dist/block-editor/block-document.d.ts +60 -0
- package/dist/block-editor/block-document.js +143 -0
- package/dist/block-editor/data-blocks.d.ts +63 -0
- package/dist/block-editor/data-blocks.js +117 -0
- package/dist/chart/chart-data.js +46 -44
- package/dist/command-menu/hotkey.js +58 -47
- package/dist/command-menu/recents.js +8 -11
- package/dist/command-menu/registry.d.ts +9 -5
- package/dist/command-menu/registry.js +36 -46
- package/dist/controller/calendar-dates.js +82 -113
- package/dist/controller/calendar-events.js +93 -88
- package/dist/controller/client-list-controller.d.ts +2 -2
- package/dist/controller/client-list-controller.js +251 -254
- package/dist/controller/data-table-columns.d.ts +2 -0
- package/dist/controller/data-table-columns.js +50 -56
- package/dist/controller/deep-equal.d.ts +1 -0
- package/dist/controller/deep-equal.js +14 -0
- package/dist/controller/filter-toggle.js +36 -44
- package/dist/controller/in-place-editor.d.ts +42 -0
- package/dist/controller/in-place-editor.js +42 -0
- package/dist/controller/infinite-list-controller.js +219 -189
- package/dist/controller/inspector.d.ts +49 -0
- package/dist/controller/inspector.js +64 -0
- package/dist/controller/job-monitor.d.ts +3 -4
- package/dist/controller/job-monitor.js +37 -47
- package/dist/controller/kanban-board.d.ts +2 -3
- package/dist/controller/kanban-board.js +43 -24
- package/dist/controller/list-controller.d.ts +5 -5
- package/dist/controller/list-controller.js +444 -400
- package/dist/controller/list-params.js +96 -87
- package/dist/controller/pivot-grid.js +89 -95
- package/dist/controller/prev-next.d.ts +2 -2
- package/dist/controller/prev-next.js +74 -54
- package/dist/controller/record-controllers.d.ts +14 -6
- package/dist/controller/record-controllers.js +288 -257
- package/dist/controller/record-diff.d.ts +21 -0
- package/dist/controller/record-diff.js +50 -0
- package/dist/controller/record-timeline.js +42 -30
- package/dist/controller/record-tree.js +57 -47
- package/dist/controller/record-workflow.d.ts +38 -16
- package/dist/controller/record-workflow.js +85 -64
- package/dist/controller/reference-many-controller.js +198 -192
- package/dist/controller/reference-one-controller.d.ts +1 -1
- package/dist/controller/reference-one-controller.js +63 -56
- package/dist/controller/saved-queries.js +104 -91
- package/dist/controller/translate-fallback.js +10 -6
- package/dist/csv/export.d.ts +2 -1
- package/dist/csv/export.js +43 -40
- package/dist/csv/import-controller.js +185 -169
- package/dist/csv/match-headers.js +29 -22
- package/dist/csv/parse-with.js +60 -54
- package/dist/csv/run-import.js +69 -72
- package/dist/csv/validate-rows.d.ts +1 -1
- package/dist/csv/validate-rows.js +65 -52
- package/dist/data/cache-ops.d.ts +5 -4
- package/dist/data/cache-ops.js +208 -188
- package/dist/data/combine-data-providers.js +25 -23
- package/dist/data/data-provider-log.d.ts +45 -0
- package/dist/data/data-provider-log.js +93 -0
- package/dist/data/errors.js +14 -10
- package/dist/data/fake-provider.d.ts +6 -3
- package/dist/data/fake-provider.js +129 -114
- package/dist/data/fetch-utils.js +61 -44
- package/dist/data/filter-branch.d.ts +5 -0
- package/dist/data/filter-branch.js +10 -0
- package/dist/data/get-many-aggregate.js +84 -76
- package/dist/data/http-error.js +17 -12
- package/dist/data/keys.js +50 -36
- package/dist/data/mutation-options.d.ts +14 -9
- package/dist/data/mutation-options.js +93 -109
- package/dist/data/query-options.js +73 -68
- package/dist/data/remove-empty.d.ts +3 -0
- package/dist/data/remove-empty.js +11 -0
- package/dist/data/undoable.d.ts +2 -2
- package/dist/data/undoable.js +190 -174
- package/dist/data/with-lifecycle-callbacks.d.ts +45 -17
- package/dist/data/with-lifecycle-callbacks.js +66 -52
- package/dist/environment/application-update-store.js +58 -55
- package/dist/environment/offline-store.js +21 -21
- package/dist/form/array-binding.d.ts +30 -11
- package/dist/form/array-binding.js +184 -166
- package/dist/form/choice-ids.d.ts +7 -0
- package/dist/form/choice-ids.js +11 -0
- package/dist/form/choices-controller.d.ts +3 -2
- package/dist/form/choices-controller.js +302 -275
- package/dist/form/date-format.d.ts +38 -0
- package/dist/form/date-format.js +89 -0
- package/dist/form/dirty-values.d.ts +2 -2
- package/dist/form/dirty-values.js +22 -11
- package/dist/form/duration.d.ts +22 -0
- package/dist/form/duration.js +49 -0
- package/dist/form/edit-create-form.d.ts +12 -5
- package/dist/form/edit-create-form.js +243 -188
- package/dist/form/event-value.d.ts +6 -0
- package/dist/form/event-value.js +17 -0
- package/dist/form/field-error.d.ts +14 -0
- package/dist/form/field-error.js +28 -0
- package/dist/form/field-name.d.ts +20 -0
- package/dist/form/field-name.js +6 -0
- package/dist/form/filter-form-values.js +63 -49
- package/dist/form/form-controller.d.ts +9 -1
- package/dist/form/form-controller.js +165 -160
- package/dist/form/form-groups.d.ts +4 -3
- package/dist/form/form-groups.js +79 -79
- package/dist/form/form-handle.d.ts +175 -0
- package/dist/form/form-handle.js +68 -0
- package/dist/form/input-binding.d.ts +56 -31
- package/dist/form/input-binding.js +169 -118
- package/dist/form/input-values.d.ts +130 -0
- package/dist/form/input-values.js +177 -0
- package/dist/form/json-text.js +51 -37
- package/dist/form/save-pipeline.d.ts +6 -8
- package/dist/form/save-pipeline.js +91 -82
- package/dist/form/set-form-values.d.ts +6 -0
- package/dist/form/set-form-values.js +10 -0
- package/dist/form/source.js +97 -65
- package/dist/form/text-of.d.ts +2 -0
- package/dist/form/text-of.js +7 -0
- package/dist/form/validators.d.ts +58 -19
- package/dist/form/validators.js +97 -71
- package/dist/geo/bbox.d.ts +5 -4
- package/dist/geo/bbox.js +69 -48
- package/dist/geo/bounds.d.ts +14 -3
- package/dist/geo/bounds.js +98 -68
- package/dist/geo/constraints.js +88 -93
- package/dist/geo/coordinates.js +31 -23
- package/dist/geo/editor.d.ts +13 -13
- package/dist/geo/editor.js +176 -153
- package/dist/geo/geocoding.d.ts +4 -4
- package/dist/geo/geocoding.js +38 -41
- package/dist/geo/geojson.d.ts +12 -74
- package/dist/geo/geojson.js +52 -40
- package/dist/geo/geometry-math.d.ts +4 -4
- package/dist/geo/geometry-math.js +100 -93
- package/dist/geo/index.d.ts +7 -6
- package/dist/geo/map-layers.d.ts +42 -7
- package/dist/geo/map-layers.js +88 -62
- package/dist/geo/map-style.js +31 -32
- package/dist/geo/messages.js +48 -45
- package/dist/geo/nominatim.js +88 -80
- package/dist/geo/osm-presets.d.ts +4 -3
- package/dist/geo/osm-presets.js +108 -86
- package/dist/geo/osm-tags.js +14 -12
- package/dist/geo/overpass.d.ts +14 -2
- package/dist/geo/overpass.js +83 -90
- package/dist/geo/shape-value.d.ts +9 -5
- package/dist/geo/shape-value.js +169 -146
- package/dist/geo/snap-to-roads.d.ts +3 -3
- package/dist/geo/snap-to-roads.js +55 -50
- package/dist/geo/throttle.js +36 -39
- package/dist/guards.d.ts +37 -0
- package/dist/guards.js +62 -0
- package/dist/i18n/default-messages.js +34 -29
- package/dist/i18n/english-messages.js +187 -189
- package/dist/i18n/i18n.d.ts +2 -2
- package/dist/i18n/i18n.js +47 -60
- package/dist/i18n/inflection.d.ts +34 -0
- package/dist/i18n/inflection.js +82 -0
- package/dist/i18n/messages.d.ts +10 -2
- package/dist/i18n/messages.js +17 -4
- package/dist/i18n/missing-keys.js +66 -62
- package/dist/i18n/translatable.d.ts +16 -1
- package/dist/i18n/translatable.js +40 -47
- package/dist/index.d.ts +71 -24
- package/dist/index.js +40 -288
- package/dist/json/deep-equal.d.ts +8 -0
- package/dist/json/deep-equal.js +24 -0
- package/dist/json/strip-json-comments.js +38 -42
- package/dist/noop.d.ts +2 -0
- package/dist/noop.js +4 -0
- package/dist/notification/error-message.d.ts +2 -0
- package/dist/notification/error-message.js +10 -0
- package/dist/notification/notification-store.d.ts +3 -2
- package/dist/notification/notification-store.js +41 -45
- package/dist/onboarding-tour/placement.js +63 -78
- package/dist/onboarding-tour/tour-controller.js +46 -54
- package/dist/realtime/add-events-for-mutations.js +75 -81
- package/dist/realtime/announce-lock.js +16 -13
- package/dist/realtime/broadcast-channel-transport.js +65 -59
- package/dist/realtime/copy-members.js +22 -14
- package/dist/realtime/in-memory-lock-provider.js +40 -48
- package/dist/realtime/live.js +95 -101
- package/dist/realtime/lock-options.js +16 -16
- package/dist/realtime/lock-session.js +91 -78
- package/dist/realtime/memory-transport.js +25 -25
- package/dist/realtime/presence.js +120 -122
- package/dist/realtime/realtime-data-provider.d.ts +10 -0
- package/dist/realtime/realtime-data-provider.js +45 -42
- package/dist/realtime/reconnect-backoff.js +17 -0
- package/dist/realtime/sse-transport.d.ts +31 -0
- package/dist/realtime/sse-transport.js +235 -0
- package/dist/realtime/status-store.js +23 -28
- package/dist/realtime/topic-hub.js +62 -71
- package/dist/realtime/topics.js +10 -12
- package/dist/realtime/types.js +10 -12
- package/dist/realtime/unread-count.js +21 -21
- package/dist/realtime/websocket-transport.d.ts +32 -0
- package/dist/realtime/websocket-transport.js +306 -0
- package/dist/resource/describe-resource.d.ts +2 -0
- package/dist/resource/describe-resource.js +232 -219
- package/dist/resource/get-at-path.js +29 -14
- package/dist/resource/identifier.d.ts +18 -0
- package/dist/resource/identifier.js +27 -0
- package/dist/resource/record.d.ts +4 -0
- package/dist/resource/record.js +10 -0
- package/dist/resource/register.d.ts +7 -0
- package/dist/resource/registry-store.d.ts +45 -15
- package/dist/resource/registry-store.js +67 -60
- package/dist/resource/registry.d.ts +10 -0
- package/dist/resource/registry.js +8 -4
- package/dist/resource/source-path.d.ts +16 -1
- package/dist/rich-text/rich-text-extras.d.ts +132 -0
- package/dist/rich-text/rich-text-extras.js +179 -0
- package/dist/rich-text/rich-text-image.d.ts +124 -0
- package/dist/rich-text/rich-text-image.js +163 -0
- package/dist/rich-text/rich-text.d.ts +6 -0
- package/dist/rich-text/rich-text.js +125 -88
- package/dist/router/location.d.ts +22 -4
- package/dist/router/location.js +41 -18
- package/dist/router/match.js +47 -39
- package/dist/router/paths.js +40 -34
- package/dist/router/record-from-location.d.ts +1 -1
- package/dist/router/record-from-location.js +25 -17
- package/dist/store/adapters.js +51 -55
- package/dist/store/micro-store.js +57 -59
- package/dist/store/shallow-equal.d.ts +12 -0
- package/dist/store/shallow-equal.js +20 -0
- package/dist/store/store.js +54 -57
- package/package.json +13 -11
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
// src/block-editor/data-blocks.ts
|
|
3
|
+
var dataBlockNames = [
|
|
4
|
+
"referenceRecord",
|
|
5
|
+
"recordList",
|
|
6
|
+
"chart"
|
|
7
|
+
];
|
|
8
|
+
var referenceRecordSchema = z.object({
|
|
9
|
+
resource: z.string().default(""),
|
|
10
|
+
id: z.union([z.string(), z.number()]).nullable().default(null),
|
|
11
|
+
fields: z.array(z.string()).optional(),
|
|
12
|
+
layout: z.enum(["card", "inline"]).default("card")
|
|
13
|
+
});
|
|
14
|
+
var recordListSchema = z.object({
|
|
15
|
+
resource: z.string().default(""),
|
|
16
|
+
perPage: z.number().int().min(1).max(50).default(5),
|
|
17
|
+
fields: z.array(z.string()).optional()
|
|
18
|
+
});
|
|
19
|
+
var chartSchema = z.object({
|
|
20
|
+
resource: z.string().default(""),
|
|
21
|
+
type: z.enum([
|
|
22
|
+
"bar",
|
|
23
|
+
"line",
|
|
24
|
+
"pie"
|
|
25
|
+
]).default("bar"),
|
|
26
|
+
category: z.string().default(""),
|
|
27
|
+
value: z.string().default(""),
|
|
28
|
+
aggregate: z.enum([
|
|
29
|
+
"count",
|
|
30
|
+
"sum",
|
|
31
|
+
"avg"
|
|
32
|
+
]).default("count"),
|
|
33
|
+
perPage: z.number().int().min(1).max(1e3).default(100)
|
|
34
|
+
});
|
|
35
|
+
var referenceRecordBlockSpec = {
|
|
36
|
+
name: "referenceRecord",
|
|
37
|
+
label: "Reference record",
|
|
38
|
+
labelKey: "chelone.block_editor.referenceRecord.label",
|
|
39
|
+
group: "data",
|
|
40
|
+
keywords: [
|
|
41
|
+
"record",
|
|
42
|
+
"relation",
|
|
43
|
+
"embed",
|
|
44
|
+
"reference"
|
|
45
|
+
],
|
|
46
|
+
description: "Embed a live record",
|
|
47
|
+
descriptionKey: "chelone.block_editor.referenceRecord.description",
|
|
48
|
+
schema: referenceRecordSchema
|
|
49
|
+
};
|
|
50
|
+
var recordListBlockSpec = {
|
|
51
|
+
name: "recordList",
|
|
52
|
+
label: "Record list",
|
|
53
|
+
labelKey: "chelone.block_editor.recordList.label",
|
|
54
|
+
group: "data",
|
|
55
|
+
keywords: [
|
|
56
|
+
"list",
|
|
57
|
+
"table",
|
|
58
|
+
"records",
|
|
59
|
+
"collection"
|
|
60
|
+
],
|
|
61
|
+
description: "Embed a live list from a resource",
|
|
62
|
+
descriptionKey: "chelone.block_editor.recordList.description",
|
|
63
|
+
schema: recordListSchema
|
|
64
|
+
};
|
|
65
|
+
var chartBlockSpec = {
|
|
66
|
+
name: "chart",
|
|
67
|
+
label: "Chart",
|
|
68
|
+
labelKey: "chelone.block_editor.chart.label",
|
|
69
|
+
group: "data",
|
|
70
|
+
keywords: [
|
|
71
|
+
"chart",
|
|
72
|
+
"graph",
|
|
73
|
+
"bar",
|
|
74
|
+
"line",
|
|
75
|
+
"pie",
|
|
76
|
+
"analytics"
|
|
77
|
+
],
|
|
78
|
+
description: "Aggregate a resource into a chart",
|
|
79
|
+
descriptionKey: "chelone.block_editor.chart.description",
|
|
80
|
+
schema: chartSchema
|
|
81
|
+
};
|
|
82
|
+
/** The fields a data block shows: the given ones, else every key but `id`, cut to `limit`. */
|
|
83
|
+
function pickDataBlockFields(record, fields, limit) {
|
|
84
|
+
const picked = fields !== void 0 && fields.length > 0 ? [...fields] : Object.keys(record).filter((key) => key !== "id");
|
|
85
|
+
return limit === void 0 ? picked : picked.slice(0, limit);
|
|
86
|
+
}
|
|
87
|
+
function formatDataBlockValue(value) {
|
|
88
|
+
if (value === null || value === void 0) return "";
|
|
89
|
+
if (typeof value !== "object" && typeof value !== "function") return String(value);
|
|
90
|
+
try {
|
|
91
|
+
return JSON.stringify(value) ?? String(value);
|
|
92
|
+
} catch {
|
|
93
|
+
return String(value);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** A record's own value for a key. A key the record does not own reads as absent. */
|
|
97
|
+
var readDataBlockValue = (row, key) => Object.getOwnPropertyDescriptor(row, key)?.value;
|
|
98
|
+
/** Group rows by `String(row[category])` in first-seen order and reduce `value` by the aggregate. */
|
|
99
|
+
function aggregateChartData(rows, { category, value, aggregate }) {
|
|
100
|
+
const groups = /* @__PURE__ */ new Map();
|
|
101
|
+
for (const row of rows) {
|
|
102
|
+
const raw = readDataBlockValue(row, category);
|
|
103
|
+
const name = raw === null || raw === void 0 ? "" : String(raw);
|
|
104
|
+
const group = groups.get(name) ?? {
|
|
105
|
+
count: 0,
|
|
106
|
+
sum: 0
|
|
107
|
+
};
|
|
108
|
+
group.count += 1;
|
|
109
|
+
group.sum += Number(readDataBlockValue(row, value)) || 0;
|
|
110
|
+
groups.set(name, group);
|
|
111
|
+
}
|
|
112
|
+
return [...groups].map(([name, { count, sum }]) => ({
|
|
113
|
+
name,
|
|
114
|
+
value: aggregate === "count" ? count : aggregate === "sum" ? sum : sum / count
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
export { aggregateChartData, chartBlockSpec, dataBlockNames, formatDataBlockValue, pickDataBlockFields, readDataBlockValue, recordListBlockSpec, referenceRecordBlockSpec };
|
package/dist/chart/chart-data.js
CHANGED
|
@@ -1,59 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
// src/chart/chart-data.ts
|
|
2
|
+
/** Theme variables the registry ships, in series order. */
|
|
3
|
+
var DEFAULT_CHART_COLORS = [
|
|
4
|
+
"var(--chart-1)",
|
|
5
|
+
"var(--chart-2)",
|
|
6
|
+
"var(--chart-3)",
|
|
7
|
+
"var(--chart-4)",
|
|
8
|
+
"var(--chart-5)"
|
|
7
9
|
];
|
|
8
|
-
|
|
10
|
+
var FALLBACK_COLOR = "var(--chart-1)";
|
|
9
11
|
function chartColor(index, colors = DEFAULT_CHART_COLORS) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
const list = colors.length > 0 ? colors : DEFAULT_CHART_COLORS;
|
|
13
|
+
const length = list.length;
|
|
14
|
+
return list[(index % length + length) % length] ?? FALLBACK_COLOR;
|
|
13
15
|
}
|
|
14
16
|
function toFiniteNumber(value) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
18
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
19
|
+
const parsed = Number(value);
|
|
20
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
21
23
|
}
|
|
22
24
|
function toCartesianRows(data, xField, yField) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
return data.map((row, index) => ({
|
|
26
|
+
index,
|
|
27
|
+
x: row[xField],
|
|
28
|
+
y: toFiniteNumber(row[yField])
|
|
29
|
+
}));
|
|
28
30
|
}
|
|
29
31
|
function hasChartValues(rows) {
|
|
30
|
-
|
|
32
|
+
return rows.some((row) => row.y !== null);
|
|
31
33
|
}
|
|
32
34
|
function toLabel(value) {
|
|
33
|
-
|
|
35
|
+
return value === void 0 || value === null ? "" : String(value);
|
|
34
36
|
}
|
|
35
37
|
function toDonutSlices(data, labelField, valueField) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
const kept = [];
|
|
39
|
+
for (const row of data) {
|
|
40
|
+
const value = toFiniteNumber(row[valueField]);
|
|
41
|
+
if (value !== null && value > 0) kept.push({
|
|
42
|
+
label: toLabel(row[labelField]),
|
|
43
|
+
value
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const total = kept.reduce((sum, slice) => sum + slice.value, 0);
|
|
47
|
+
if (total <= 0) return [];
|
|
48
|
+
return kept.map((slice) => ({
|
|
49
|
+
...slice,
|
|
50
|
+
share: slice.value / total
|
|
51
|
+
}));
|
|
46
52
|
}
|
|
53
|
+
/** `delta` is a ratio (0.05 is 5.0%). Direction follows the sign of `delta`, not of the rounded text. */
|
|
47
54
|
function getMetricDelta(delta) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
if (delta === void 0 || !Number.isFinite(delta)) return null;
|
|
56
|
+
return {
|
|
57
|
+
direction: delta > 0 ? "up" : delta < 0 ? "down" : "flat",
|
|
58
|
+
text: `${(delta * 100).toFixed(1)}%`
|
|
59
|
+
};
|
|
51
60
|
}
|
|
52
|
-
export {
|
|
53
|
-
DEFAULT_CHART_COLORS,
|
|
54
|
-
chartColor,
|
|
55
|
-
getMetricDelta,
|
|
56
|
-
hasChartValues,
|
|
57
|
-
toCartesianRows,
|
|
58
|
-
toDonutSlices
|
|
59
|
-
};
|
|
61
|
+
export { DEFAULT_CHART_COLORS, chartColor, getMetricDelta, hasChartValues, toCartesianRows, toDonutSlices };
|
|
@@ -1,60 +1,71 @@
|
|
|
1
|
+
// src/command-menu/hotkey.ts
|
|
1
2
|
function parseBinding(binding) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
};
|
|
3
|
+
const lower = binding.toLowerCase();
|
|
4
|
+
const plus = lower === "+" || lower.endsWith("++");
|
|
5
|
+
const parts = (plus ? lower.slice(0, -1) : lower).split("+");
|
|
6
|
+
return {
|
|
7
|
+
key: plus ? "+" : parts.pop() ?? "",
|
|
8
|
+
mod: parts.includes("mod"),
|
|
9
|
+
shift: parts.includes("shift"),
|
|
10
|
+
alt: parts.includes("alt")
|
|
11
|
+
};
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
13
|
+
var punctuationCodes = {
|
|
14
|
+
"/": "Slash",
|
|
15
|
+
".": "Period",
|
|
16
|
+
",": "Comma",
|
|
17
|
+
";": "Semicolon",
|
|
18
|
+
"'": "Quote",
|
|
19
|
+
"[": "BracketLeft",
|
|
20
|
+
"]": "BracketRight",
|
|
21
|
+
"\\": "Backslash",
|
|
22
|
+
"-": "Minus",
|
|
23
|
+
"=": "Equal",
|
|
24
|
+
"`": "Backquote"
|
|
25
25
|
};
|
|
26
26
|
function codeFor(key) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
if (/^[a-z]$/.test(key)) return `Key${key.toUpperCase()}`;
|
|
28
|
+
if (/^[0-9]$/.test(key)) return `Digit${key}`;
|
|
29
|
+
return punctuationCodes[key];
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Option and Shift rewrite `event.key` ("÷" for Option+/, "!" for Shift+1), so
|
|
33
|
+
* a chord with either falls back to the physical key, but only when `key` is
|
|
34
|
+
* not already a letter or digit. On a Dvorak layout the physical K key types
|
|
35
|
+
* "t": that stays a "t", so `mod+k` does not fire on it.
|
|
36
|
+
*/
|
|
31
37
|
function keyMatches(event, parsed, eventKey) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
if (eventKey === parsed.key) return true;
|
|
39
|
+
if (!(parsed.alt || parsed.shift) || !event.code) return false;
|
|
40
|
+
if (/^[a-z0-9]$/i.test(event.key)) return false;
|
|
41
|
+
return event.code === codeFor(parsed.key);
|
|
36
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Reads bindings such as `"mod+k"`, `"mod+shift+p"`, `"alt+/"` and `"f1"`.
|
|
45
|
+
* `mod` is Meta or Ctrl. Modifiers match exactly: a binding without `mod`
|
|
46
|
+
* does not fire while Ctrl or Meta is down.
|
|
47
|
+
*/
|
|
37
48
|
function matchesHotkey(event, binding) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return modDown === parsed.mod && event.shiftKey === parsed.shift && event.altKey === parsed.alt;
|
|
49
|
+
const parsed = parseBinding(binding);
|
|
50
|
+
if (!keyMatches(event, parsed, event.key.toLowerCase())) return false;
|
|
51
|
+
return (event.metaKey || event.ctrlKey) === parsed.mod && event.shiftKey === parsed.shift && event.altKey === parsed.alt;
|
|
42
52
|
}
|
|
43
53
|
function isTypingTarget(target) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
if (!target) return false;
|
|
55
|
+
if (target.isContentEditable) return true;
|
|
56
|
+
const tag = target.tagName?.toLowerCase();
|
|
57
|
+
return tag === "input" || tag === "textarea" || tag === "select";
|
|
48
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* `matchesHotkey` plus two guards: nothing fires during IME composition, and a
|
|
61
|
+
* binding with neither `mod` nor `alt` is ignored while `target` is typing, so
|
|
62
|
+
* `/` never opens the menu from a text field.
|
|
63
|
+
*/
|
|
49
64
|
function shouldHandleHotkey(event, binding, target) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
65
|
+
if (event.isComposing) return false;
|
|
66
|
+
if (!matchesHotkey(event, binding)) return false;
|
|
67
|
+
const { mod, alt } = parseBinding(binding);
|
|
68
|
+
if (!mod && !alt && isTypingTarget(target)) return false;
|
|
69
|
+
return true;
|
|
55
70
|
}
|
|
56
|
-
export {
|
|
57
|
-
isTypingTarget,
|
|
58
|
-
matchesHotkey,
|
|
59
|
-
shouldHandleHotkey
|
|
60
|
-
};
|
|
71
|
+
export { isTypingTarget, matchesHotkey, shouldHandleHotkey };
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
// src/command-menu/recents.ts
|
|
2
|
+
/** Where the command menu keeps its recents in a `CheloneStore`. */
|
|
3
|
+
var recentsKey = "command-menu.recents";
|
|
2
4
|
function sameEntry(a, b) {
|
|
3
|
-
|
|
5
|
+
return a.type === b.type && a.resource === b.resource && (a.id === void 0 ? void 0 : String(a.id)) === (b.id === void 0 ? void 0 : String(b.id));
|
|
4
6
|
}
|
|
7
|
+
/** Puts `entry` first, drops an earlier copy, and cuts to `limit`. */
|
|
5
8
|
function rememberRecent(recents, entry, limit) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
0,
|
|
9
|
-
limit
|
|
10
|
-
);
|
|
9
|
+
if (limit < 1) return [];
|
|
10
|
+
return [entry, ...recents.filter((other) => !sameEntry(other, entry))].slice(0, limit);
|
|
11
11
|
}
|
|
12
|
-
export {
|
|
13
|
-
recentsKey,
|
|
14
|
-
rememberRecent
|
|
15
|
-
};
|
|
12
|
+
export { recentsKey, rememberRecent };
|
|
@@ -3,7 +3,7 @@ import { type MicroStore } from "../store/micro-store.js";
|
|
|
3
3
|
* An extra command in the menu. `label` and `icon` are generic so each adapter
|
|
4
4
|
* uses its own types (a React node, a Vue component).
|
|
5
5
|
*/
|
|
6
|
-
export interface CommandAction<TLabel
|
|
6
|
+
export interface CommandAction<TLabel, TIcon> {
|
|
7
7
|
id: string;
|
|
8
8
|
label: TLabel;
|
|
9
9
|
icon?: TIcon;
|
|
@@ -15,7 +15,7 @@ export interface CommandAction<TLabel = string, TIcon = unknown> {
|
|
|
15
15
|
when?: () => boolean;
|
|
16
16
|
onSelect: () => void;
|
|
17
17
|
}
|
|
18
|
-
export interface CommandRegistry<TLabel
|
|
18
|
+
export interface CommandRegistry<TLabel, TIcon> {
|
|
19
19
|
store: MicroStore<{
|
|
20
20
|
commands: CommandAction<TLabel, TIcon>[];
|
|
21
21
|
}>;
|
|
@@ -28,7 +28,11 @@ export interface CommandRegistry<TLabel = string, TIcon = unknown> {
|
|
|
28
28
|
unregister(id: string): void;
|
|
29
29
|
}
|
|
30
30
|
export declare const defaultCommandGroup = "actions";
|
|
31
|
-
export declare function createCommandRegistry<TLabel
|
|
32
|
-
export declare function getVisibleCommands<T extends
|
|
31
|
+
export declare function createCommandRegistry<TLabel, TIcon>(): CommandRegistry<TLabel, TIcon>;
|
|
32
|
+
export declare function getVisibleCommands<T extends {
|
|
33
|
+
when?: () => boolean;
|
|
34
|
+
}>(commands: readonly T[]): T[];
|
|
33
35
|
/** Groups in first-seen order. A missing `group` reads as `"actions"`. */
|
|
34
|
-
export declare function groupCommands<T extends
|
|
36
|
+
export declare function groupCommands<T extends {
|
|
37
|
+
group?: string;
|
|
38
|
+
}>(commands: readonly T[]): [string, T[]][];
|
|
@@ -1,53 +1,43 @@
|
|
|
1
1
|
import { createMicroStore } from "../store/micro-store.js";
|
|
2
|
-
|
|
2
|
+
// src/command-menu/registry.ts
|
|
3
|
+
var defaultCommandGroup = "actions";
|
|
3
4
|
function createCommandRegistry() {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return () => {
|
|
22
|
-
store.setState((prev) => ({
|
|
23
|
-
commands: prev.commands.filter((command) => command !== action)
|
|
24
|
-
}));
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
return { store, register, unregister };
|
|
5
|
+
const store = createMicroStore({ commands: [] });
|
|
6
|
+
const unregister = (id) => {
|
|
7
|
+
store.setState((prev) => ({ commands: prev.commands.filter((command) => command.id !== id) }));
|
|
8
|
+
};
|
|
9
|
+
const register = (action) => {
|
|
10
|
+
store.setState((prev) => {
|
|
11
|
+
return { commands: prev.commands.some((command) => command.id === action.id) ? prev.commands.map((command) => command.id === action.id ? action : command) : [...prev.commands, action] };
|
|
12
|
+
});
|
|
13
|
+
return () => {
|
|
14
|
+
store.setState((prev) => ({ commands: prev.commands.filter((command) => command !== action) }));
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
store,
|
|
19
|
+
register,
|
|
20
|
+
unregister
|
|
21
|
+
};
|
|
28
22
|
}
|
|
29
23
|
function getVisibleCommands(commands) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
return commands.filter((command) => {
|
|
25
|
+
try {
|
|
26
|
+
return command.when ? command.when() !== false : true;
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
37
31
|
}
|
|
32
|
+
/** Groups in first-seen order. A missing `group` reads as `"actions"`. */
|
|
38
33
|
function groupCommands(commands) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
34
|
+
const groups = /* @__PURE__ */ new Map();
|
|
35
|
+
for (const command of commands) {
|
|
36
|
+
const group = command.group ?? "actions";
|
|
37
|
+
const members = groups.get(group);
|
|
38
|
+
if (members) members.push(command);
|
|
39
|
+
else groups.set(group, [command]);
|
|
40
|
+
}
|
|
41
|
+
return [...groups];
|
|
47
42
|
}
|
|
48
|
-
export {
|
|
49
|
-
createCommandRegistry,
|
|
50
|
-
defaultCommandGroup,
|
|
51
|
-
getVisibleCommands,
|
|
52
|
-
groupCommands
|
|
53
|
-
};
|
|
43
|
+
export { createCommandRegistry, defaultCommandGroup, getVisibleCommands, groupCommands };
|