@chelone/core 0.6.0 → 0.9.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 +23 -17
- package/dist/auth/auth-options.js +82 -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 +64 -0
- package/dist/block-editor/block-document.js +147 -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.d.ts +4 -0
- package/dist/command-menu/recents.js +12 -11
- package/dist/command-menu/registry.d.ts +9 -5
- package/dist/command-menu/registry.js +36 -46
- package/dist/controller/calendar-dates.d.ts +12 -0
- package/dist/controller/calendar-dates.js +94 -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 +43 -0
- package/dist/controller/in-place-editor.js +43 -0
- package/dist/controller/infinite-list-controller.js +219 -189
- package/dist/controller/inspector.d.ts +55 -0
- package/dist/controller/inspector.js +70 -0
- package/dist/controller/job-monitor.d.ts +10 -4
- package/dist/controller/job-monitor.js +46 -47
- package/dist/controller/kanban-board.js +45 -24
- package/dist/controller/list-controller.d.ts +8 -5
- package/dist/controller/list-controller.js +447 -400
- package/dist/controller/list-params.js +96 -87
- package/dist/controller/pivot-grid.d.ts +4 -4
- 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.d.ts +5 -5
- 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.d.ts +2 -0
- package/dist/controller/reference-many-controller.js +199 -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 +4 -3
- package/dist/csv/export.js +43 -40
- package/dist/csv/import-controller.d.ts +4 -0
- package/dist/csv/import-controller.js +189 -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 +135 -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 +22 -2
- package/dist/form/choices-controller.js +315 -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 +171 -118
- package/dist/form/input-values.d.ts +140 -0
- package/dist/form/input-values.js +206 -0
- package/dist/form/json-text.d.ts +11 -0
- package/dist/form/json-text.js +62 -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.d.ts +10 -0
- package/dist/geo/coordinates.js +44 -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 +9 -8
- package/dist/geo/map-layers.d.ts +42 -7
- package/dist/geo/map-layers.js +88 -62
- package/dist/geo/map-style.d.ts +12 -0
- package/dist/geo/map-style.js +43 -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 +44 -0
- package/dist/guards.js +77 -0
- package/dist/i18n/default-messages.js +34 -29
- package/dist/i18n/english-messages.d.ts +172 -0
- package/dist/i18n/english-messages.js +359 -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.d.ts +4 -0
- package/dist/i18n/missing-keys.js +70 -62
- package/dist/i18n/translatable.d.ts +16 -1
- package/dist/i18n/translatable.js +40 -47
- package/dist/index.d.ts +87 -39
- package/dist/index.js +54 -301
- 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.d.ts +2 -0
- package/dist/onboarding-tour/placement.js +65 -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 +247 -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 +23 -2
- package/dist/rich-text/rich-text-extras.d.ts +134 -0
- package/dist/rich-text/rich-text-extras.js +181 -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 +24 -4
- package/dist/router/location.js +43 -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/dist/ui-defaults.d.ts +15 -0
- package/dist/ui-defaults.js +17 -0
- package/package.json +13 -11
|
@@ -10,12 +10,44 @@
|
|
|
10
10
|
* a plain keyed store with a whole-map snapshot, mirroring the store /
|
|
11
11
|
* MicroStore notification pattern.
|
|
12
12
|
*/
|
|
13
|
+
/**
|
|
14
|
+
* The augmentation point for the types a resource definition carries that
|
|
15
|
+
* depend on the UI framework. An adapter names them once, as `Register` does
|
|
16
|
+
* for resources:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* declare module "@chelone/core" {
|
|
20
|
+
* interface ResourceSlots {
|
|
21
|
+
* icon: ComponentType;
|
|
22
|
+
* representation: RecordRepresentation;
|
|
23
|
+
* component: ComponentType;
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Augmenting is global to a TypeScript program, so a program that includes
|
|
29
|
+
* both adapters would see both. Without an augmentation each type is
|
|
30
|
+
* `unknown`.
|
|
31
|
+
*/
|
|
32
|
+
export interface ResourceSlots {
|
|
33
|
+
}
|
|
34
|
+
/** A resource's menu icon: the adapter's icon type, or `unknown` when none is declared. */
|
|
35
|
+
export type ResourceIcon = ResourceSlots extends {
|
|
36
|
+
icon: infer TIcon;
|
|
37
|
+
} ? TIcon : unknown;
|
|
38
|
+
/** How a record renders as a label: the adapter's type, or `unknown` when none is declared. */
|
|
39
|
+
export type ResourceRepresentation = ResourceSlots extends {
|
|
40
|
+
representation: infer TRepresentation;
|
|
41
|
+
} ? TRepresentation : unknown;
|
|
42
|
+
/** A resource's list, create, edit or show view: the adapter's component type, or `unknown` when none is declared. */
|
|
43
|
+
export type ResourceComponent = ResourceSlots extends {
|
|
44
|
+
component: infer TComponent;
|
|
45
|
+
} ? TComponent : unknown;
|
|
13
46
|
/**
|
|
14
47
|
* What a single mounted resource exposes to the shell. `hasList/hasCreate/…`
|
|
15
|
-
* drive the routing engine and the menu; `icon` / `recordRepresentation`
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* shadcn menu reads `options.group`).
|
|
48
|
+
* drive the routing engine and the menu; `icon` / `recordRepresentation` take
|
|
49
|
+
* the adapter's types through {@link ResourceSlots}. `options` is an open
|
|
50
|
+
* extension bag (D9: the shadcn menu reads `options.group`).
|
|
19
51
|
*/
|
|
20
52
|
export interface ResourceDefinition {
|
|
21
53
|
name: string;
|
|
@@ -23,10 +55,8 @@ export interface ResourceDefinition {
|
|
|
23
55
|
hasCreate: boolean;
|
|
24
56
|
hasEdit: boolean;
|
|
25
57
|
hasShow: boolean;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
/** Framework-neutral: `string | function | ReactNode` in `@chelone/react`. */
|
|
29
|
-
recordRepresentation?: unknown;
|
|
58
|
+
icon?: ResourceIcon;
|
|
59
|
+
recordRepresentation?: ResourceRepresentation;
|
|
30
60
|
/** Open extension bag — e.g. `{ group: "content" }` for menu grouping (D9). */
|
|
31
61
|
options?: Record<string, unknown>;
|
|
32
62
|
}
|
|
@@ -60,18 +90,18 @@ export interface ResourceRegistry {
|
|
|
60
90
|
/** Framework-neutral props `computeDefinition` derives a definition from. */
|
|
61
91
|
export interface ComputeDefinitionProps {
|
|
62
92
|
name: string;
|
|
63
|
-
/** A list component
|
|
64
|
-
list?:
|
|
65
|
-
create?:
|
|
66
|
-
edit?:
|
|
67
|
-
show?:
|
|
93
|
+
/** A list component; presence sets `hasList`. */
|
|
94
|
+
list?: ResourceComponent;
|
|
95
|
+
create?: ResourceComponent;
|
|
96
|
+
edit?: ResourceComponent;
|
|
97
|
+
show?: ResourceComponent;
|
|
68
98
|
/** Explicit overrides OR-ed with component presence (`!!list || !!hasList`). */
|
|
69
99
|
hasList?: boolean;
|
|
70
100
|
hasCreate?: boolean;
|
|
71
101
|
hasEdit?: boolean;
|
|
72
102
|
hasShow?: boolean;
|
|
73
|
-
icon?:
|
|
74
|
-
recordRepresentation?:
|
|
103
|
+
icon?: ResourceIcon;
|
|
104
|
+
recordRepresentation?: ResourceRepresentation;
|
|
75
105
|
options?: Record<string, unknown>;
|
|
76
106
|
}
|
|
77
107
|
/**
|
|
@@ -1,68 +1,75 @@
|
|
|
1
|
+
// src/resource/registry-store.ts
|
|
2
|
+
/**
|
|
3
|
+
* Derive a `ResourceDefinition` from resource props: a route flag is true when
|
|
4
|
+
* EITHER the component is present OR the explicit `has*` flag is set
|
|
5
|
+
* (`hasList = !!list || !!hasList`), matching ra-core's Resource registration.
|
|
6
|
+
*/
|
|
1
7
|
function computeDefinition(props) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
return {
|
|
9
|
+
name: props.name,
|
|
10
|
+
hasList: !!props.list || !!props.hasList,
|
|
11
|
+
hasCreate: !!props.create || !!props.hasCreate,
|
|
12
|
+
hasEdit: !!props.edit || !!props.hasEdit,
|
|
13
|
+
hasShow: !!props.show || !!props.hasShow,
|
|
14
|
+
icon: props.icon,
|
|
15
|
+
recordRepresentation: props.recordRepresentation,
|
|
16
|
+
options: props.options
|
|
17
|
+
};
|
|
12
18
|
}
|
|
19
|
+
/** Shallow-compare two `options` bags (own enumerable keys, `Object.is` values). */
|
|
13
20
|
function optionsEqual(a, b) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
return true;
|
|
21
|
+
if (a === b) return true;
|
|
22
|
+
if (a == null || b == null) return false;
|
|
23
|
+
const aKeys = Object.keys(a);
|
|
24
|
+
const bKeys = Object.keys(b);
|
|
25
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
26
|
+
for (const key of aKeys) if (!Object.is(a[key], b[key])) return false;
|
|
27
|
+
return true;
|
|
23
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Whether two definitions are observably equal: scalar fields by `Object.is`,
|
|
31
|
+
* `options` shallow-compared. Used to no-op needless re-registrations so the
|
|
32
|
+
* React register-on-render loop doesn't churn subscribers (parity §0a-8).
|
|
33
|
+
*/
|
|
24
34
|
function definitionEqual(a, b) {
|
|
25
|
-
|
|
35
|
+
return a.name === b.name && a.hasList === b.hasList && a.hasCreate === b.hasCreate && a.hasEdit === b.hasEdit && a.hasShow === b.hasShow && Object.is(a.icon, b.icon) && Object.is(a.recordRepresentation, b.recordRepresentation) && optionsEqual(a.options, b.options);
|
|
26
36
|
}
|
|
27
37
|
function createResourceRegistry() {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
38
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
39
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
40
|
+
let snapshot = null;
|
|
41
|
+
const notify = () => {
|
|
42
|
+
snapshot = null;
|
|
43
|
+
for (const listener of listeners) listener();
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
register(definition) {
|
|
47
|
+
const current = definitions.get(definition.name);
|
|
48
|
+
if (current && definitionEqual(current, definition)) return;
|
|
49
|
+
definitions.set(definition.name, definition);
|
|
50
|
+
notify();
|
|
51
|
+
},
|
|
52
|
+
unregister(name) {
|
|
53
|
+
if (!definitions.delete(name)) return;
|
|
54
|
+
notify();
|
|
55
|
+
},
|
|
56
|
+
get(name) {
|
|
57
|
+
return definitions.get(name);
|
|
58
|
+
},
|
|
59
|
+
getAll() {
|
|
60
|
+
if (snapshot === null) {
|
|
61
|
+
const next = {};
|
|
62
|
+
for (const [name, definition] of definitions) next[name] = definition;
|
|
63
|
+
snapshot = next;
|
|
64
|
+
}
|
|
65
|
+
return snapshot;
|
|
66
|
+
},
|
|
67
|
+
subscribe(listener) {
|
|
68
|
+
listeners.add(listener);
|
|
69
|
+
return () => {
|
|
70
|
+
listeners.delete(listener);
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
};
|
|
64
74
|
}
|
|
65
|
-
export {
|
|
66
|
-
computeDefinition,
|
|
67
|
-
createResourceRegistry
|
|
68
|
-
};
|
|
75
|
+
export { computeDefinition, createResourceRegistry };
|
|
@@ -35,6 +35,16 @@ type Checked<T extends ResourceMap> = {
|
|
|
35
35
|
};
|
|
36
36
|
};
|
|
37
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* A resource map whose every resource has `TRecord` as its record. A view
|
|
40
|
+
* generic over its record passes it to a hook that is generic over a map, so
|
|
41
|
+
* `RecordOf<RecordResources<TRecord>, string>` is `TRecord`.
|
|
42
|
+
*/
|
|
43
|
+
export type RecordResources<TRecord> = {
|
|
44
|
+
[name: string]: {
|
|
45
|
+
schema: StandardSchemaV1<unknown, TRecord>;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
38
48
|
/**
|
|
39
49
|
* The record type for a given resource name in a registry:
|
|
40
50
|
* the schema's Standard Schema output type when a schema is present, else `LooseRecord`.
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
// src/resource/registry.ts
|
|
2
|
+
/**
|
|
3
|
+
* Identity helper that pins literal resource names + schema types via `const`.
|
|
4
|
+
* `defineResources({ posts: { schema: postSchema } })` keeps `"posts"` literal
|
|
5
|
+
* and `typeof postSchema` exact, enabling end-to-end inference.
|
|
6
|
+
*/
|
|
1
7
|
function defineResources(resources) {
|
|
2
|
-
|
|
8
|
+
return resources;
|
|
3
9
|
}
|
|
4
|
-
export {
|
|
5
|
-
defineResources
|
|
6
|
-
};
|
|
10
|
+
export { defineResources };
|
|
@@ -56,16 +56,37 @@ export type SourcePath<TRecord> = unknown extends TRecord ? string : Paths<TReco
|
|
|
56
56
|
/**
|
|
57
57
|
* The value type at `TPath`, including the `null`/`undefined` of every parent
|
|
58
58
|
* along it — what reading `record.author?.name` gives. `unknown` for a loose
|
|
59
|
-
* record.
|
|
59
|
+
* record, and for a path typed as a bare `string` (no path to walk).
|
|
60
|
+
*
|
|
61
|
+
* The guard is load-bearing even though `Get` makes the same check: without it
|
|
62
|
+
* here, `Get` over a field's open `TRecord` hangs `vue-tsc`.
|
|
60
63
|
*/
|
|
61
64
|
export type SourceValue<TRecord, TPath extends string> = unknown extends TRecord ? unknown : Get<TRecord, TPath, {
|
|
62
65
|
nullish: "path";
|
|
63
66
|
}>;
|
|
67
|
+
/**
|
|
68
|
+
* The value a `format`/`parse` transform carries at `TPath`.
|
|
69
|
+
*
|
|
70
|
+
* {@link SourceValue} walks from the record root, so a source relative to a row
|
|
71
|
+
* (`qty` under `items[]`) or a filter key (`published_at_gte`) walks to `never`.
|
|
72
|
+
* Both are legal {@link SourceProp}s and their value is simply unknown to the
|
|
73
|
+
* type, so a transform over one stays loose instead of demanding a `never` no
|
|
74
|
+
* callback can return.
|
|
75
|
+
*/
|
|
76
|
+
export type SourceTransformValue<TRecord, TPath extends string> = [
|
|
77
|
+
SourceValue<TRecord, TPath>
|
|
78
|
+
] extends [never] ? unknown : SourceValue<TRecord, TPath>;
|
|
64
79
|
/**
|
|
65
80
|
* The top-level keys of `TRecord`, for props that read or write one flat key
|
|
66
81
|
* (a filter's field, a reference's target). `string` for a loose record.
|
|
82
|
+
*
|
|
83
|
+
* The guard is {@link SourceProp}'s, not the `unknown extends` the other path
|
|
84
|
+
* types use: `keyof TRecord` of an unresolved type parameter resolves to the
|
|
85
|
+
* keys of its constraint, and every shared record type is now bounded by
|
|
86
|
+
* `ResourceRecord`, whose only key is `id`. A generic field's sibling prop
|
|
87
|
+
* (`limitSource`) would then take `"id"` and nothing else.
|
|
67
88
|
*/
|
|
68
|
-
export type SourceKey<TRecord> =
|
|
89
|
+
export type SourceKey<TRecord> = LooseRecord extends TRecord ? string : keyof TRecord & string;
|
|
69
90
|
/**
|
|
70
91
|
* The paths *relative to an array item* of every array in `TRecord`: `sku` is
|
|
71
92
|
* one for a record with `items: { sku: string }[]`.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { type RichTextToolbarGroup } from "./rich-text.js";
|
|
2
|
+
/** The image size every Chelone editor offers: 5 MB. */
|
|
3
|
+
export declare const MAX_IMAGE_BYTES: number;
|
|
4
|
+
export interface RichTextImageRules {
|
|
5
|
+
/** Exact types, or `type/*` for any subtype. SVG never passes. */
|
|
6
|
+
allowedMimeTypes: readonly string[];
|
|
7
|
+
/** Bytes. */
|
|
8
|
+
maxFileSize: number;
|
|
9
|
+
allowBase64: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** shadmin's image settings: any image, 5 MB, base64 allowed. */
|
|
12
|
+
export declare const defaultRichTextImageRules: Readonly<RichTextImageRules>;
|
|
13
|
+
/** Why an image file is refused, or `null` when it is accepted. */
|
|
14
|
+
export declare function checkRichTextImageFile(file: {
|
|
15
|
+
type: string;
|
|
16
|
+
size: number;
|
|
17
|
+
}, rules: RichTextImageRules): "type" | "size" | null;
|
|
18
|
+
export interface RichTextUpload {
|
|
19
|
+
id: string | null;
|
|
20
|
+
src: string;
|
|
21
|
+
}
|
|
22
|
+
/** What an upload handler may return: a URL, or a URL with the stored file's id. */
|
|
23
|
+
export declare function normalizeRichTextUpload(result: string | {
|
|
24
|
+
id: string | number;
|
|
25
|
+
src: string;
|
|
26
|
+
}, rules?: Pick<RichTextImageRules, "allowBase64">): RichTextUpload | null;
|
|
27
|
+
/** `width` rounded and held in `min`..`max`. `max` wins when it is below `min`. */
|
|
28
|
+
export declare function clampRichTextImageWidth(width: number, { min, max }: {
|
|
29
|
+
min: number;
|
|
30
|
+
max: number;
|
|
31
|
+
}): number;
|
|
32
|
+
/** `<name>.<extension>` for downloading an image, from its alt text and mime type. */
|
|
33
|
+
export declare function richTextImageFileName(alt: string, mimeType: string): string;
|
|
34
|
+
export type RichTextImageActionId = "view" | "download" | "copyImage" | "copyLink" | "remove";
|
|
35
|
+
export interface RichTextImageAction {
|
|
36
|
+
id: RichTextImageActionId;
|
|
37
|
+
/** Translation key, always `chelone.rich_text.image.<id>`. */
|
|
38
|
+
labelKey: `chelone.rich_text.image.${RichTextImageActionId}`;
|
|
39
|
+
defaultLabel: string;
|
|
40
|
+
}
|
|
41
|
+
/** The image popover's actions, in display order. */
|
|
42
|
+
export declare const richTextImageActions: readonly Readonly<RichTextImageAction>[];
|
|
43
|
+
export type RichTextColorPalette = "default" | "bright" | "soft" | "muted";
|
|
44
|
+
export interface RichTextColor {
|
|
45
|
+
id: string;
|
|
46
|
+
palette: RichTextColorPalette;
|
|
47
|
+
/** Translation key, always `chelone.rich_text.color.<id>`. */
|
|
48
|
+
labelKey: `chelone.rich_text.color.${string}`;
|
|
49
|
+
defaultLabel: string;
|
|
50
|
+
/** A `var(--rich-text-color-<id>)` reference, or `null` for the entry that clears the color. */
|
|
51
|
+
value: `var(--rich-text-color-${string})` | null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The color picker's entries: `default` (clears the color), then three
|
|
55
|
+
* palettes of seven. The adapter's stylesheet defines each
|
|
56
|
+
* `--rich-text-color-<id>`.
|
|
57
|
+
*/
|
|
58
|
+
export declare const richTextColors: readonly Readonly<RichTextColor>[];
|
|
59
|
+
/**
|
|
60
|
+
* `color: <value>` when `style` is one `color` declaration with a palette
|
|
61
|
+
* `var(...)`, hex, `rgb()`, `rgba()`, `hsl()` or `hsla()` value, else `null`.
|
|
62
|
+
* Everything else a style attribute can carry is dropped.
|
|
63
|
+
*/
|
|
64
|
+
export declare function sanitizeRichTextStyle(style: string): string | null;
|
|
65
|
+
/**
|
|
66
|
+
* `richTextSanitizeOptions` with `style` allowed through, for adapters that
|
|
67
|
+
* run `sanitizeRichTextStyle` on every `style` attribute.
|
|
68
|
+
*/
|
|
69
|
+
export declare const richTextStyledSanitizeOptions: Readonly<{
|
|
70
|
+
FORBID_TAGS: readonly ["style", "form", "input", "button", "textarea", "select", "iframe", "object", "embed"];
|
|
71
|
+
FORBID_ATTR: readonly string[];
|
|
72
|
+
}>;
|
|
73
|
+
/** Options for tiptap's `CodeBlockLowlight.configure`, as shadmin sets them. */
|
|
74
|
+
export declare const richTextCodeBlockOptions: Readonly<{
|
|
75
|
+
languageClassPrefix: "language-";
|
|
76
|
+
defaultLanguage: null;
|
|
77
|
+
exitOnTripleEnter: true;
|
|
78
|
+
exitOnArrowDown: true;
|
|
79
|
+
enableTabIndentation: false;
|
|
80
|
+
tabSize: 4;
|
|
81
|
+
}>;
|
|
82
|
+
export type RichTextExtraToolbarItemId = "image" | "color";
|
|
83
|
+
export interface RichTextExtraToolbarItem {
|
|
84
|
+
id: RichTextExtraToolbarItemId;
|
|
85
|
+
group: RichTextToolbarGroup;
|
|
86
|
+
/** Translation key, always `chelone.rich_text.<id>`. */
|
|
87
|
+
labelKey: `chelone.rich_text.${RichTextExtraToolbarItemId}`;
|
|
88
|
+
defaultLabel: string;
|
|
89
|
+
/** A binding in the syntax of `matchesHotkey`, e.g. `"mod+b"`. */
|
|
90
|
+
shortcut?: string;
|
|
91
|
+
}
|
|
92
|
+
/** The toolbar buttons an image or color extension adds, shaped like `richTextToolbarItems`. */
|
|
93
|
+
export declare const richTextExtraToolbarItems: readonly Readonly<RichTextExtraToolbarItem>[];
|
|
94
|
+
interface NamedMark {
|
|
95
|
+
type: {
|
|
96
|
+
name: string;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** The slice of a ProseMirror state `endColorOnEnter` reads, so core names no ProseMirror type. */
|
|
100
|
+
interface EnterState<Mark extends NamedMark, Transaction> {
|
|
101
|
+
selection: {
|
|
102
|
+
$from: {
|
|
103
|
+
marks(): readonly Mark[];
|
|
104
|
+
nodeAfter: {
|
|
105
|
+
marks: readonly Mark[];
|
|
106
|
+
} | null;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
storedMarks: readonly Mark[] | null;
|
|
110
|
+
tr: {
|
|
111
|
+
setStoredMarks(marks: readonly Mark[] | null): Transaction;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The two ProseMirror plugin hooks that end a text color with its line.
|
|
116
|
+
* Splitting a block would carry the color into the next one, so after Enter
|
|
117
|
+
* the color's `textStyle` mark is dropped from the stored marks, unless colored
|
|
118
|
+
* text follows the cursor. An adapter wires them into
|
|
119
|
+
* `new Plugin({ props: { handleKeyDown }, appendTransaction })` and runs that
|
|
120
|
+
* plugin before the keymap, which handles Enter.
|
|
121
|
+
*/
|
|
122
|
+
export declare function endColorOnEnter(): {
|
|
123
|
+
handleKeyDown<View>(_view: View, event: {
|
|
124
|
+
key: string;
|
|
125
|
+
shiftKey: boolean;
|
|
126
|
+
ctrlKey: boolean;
|
|
127
|
+
metaKey: boolean;
|
|
128
|
+
altKey: boolean;
|
|
129
|
+
}): false;
|
|
130
|
+
appendTransaction<Mark extends NamedMark, Transaction, PreviousState>(transactions: readonly {
|
|
131
|
+
docChanged: boolean;
|
|
132
|
+
}[], _previous: PreviousState, state: EnterState<Mark, Transaction>): Transaction | null;
|
|
133
|
+
};
|
|
134
|
+
export {};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { isSafeImageSrc, richTextSanitizeOptions } from "./rich-text.js";
|
|
2
|
+
// src/rich-text/rich-text-extras.ts
|
|
3
|
+
var CONTROL = /[\u0000-\u001f\u007f]/g;
|
|
4
|
+
/** The image size every Chelone editor offers: 5 MB. */
|
|
5
|
+
var MAX_IMAGE_BYTES = 5242880;
|
|
6
|
+
/** shadmin's image settings: any image, 5 MB, base64 allowed. */
|
|
7
|
+
var defaultRichTextImageRules = Object.freeze({
|
|
8
|
+
allowedMimeTypes: Object.freeze(["image/*"]),
|
|
9
|
+
maxFileSize: MAX_IMAGE_BYTES,
|
|
10
|
+
allowBase64: true
|
|
11
|
+
});
|
|
12
|
+
/** The `type/subtype` of a MIME type, lowercased, without parameters such as `;charset=utf-8`. */
|
|
13
|
+
function baseMimeType(mimeType) {
|
|
14
|
+
return (mimeType.split(";")[0] ?? "").trim().toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
function mimeMatches(pattern, type) {
|
|
17
|
+
const wanted = pattern.toLowerCase();
|
|
18
|
+
if (wanted.endsWith("/*")) return type.startsWith(wanted.slice(0, -1));
|
|
19
|
+
return type === wanted;
|
|
20
|
+
}
|
|
21
|
+
/** Why an image file is refused, or `null` when it is accepted. */
|
|
22
|
+
function checkRichTextImageFile(file, rules) {
|
|
23
|
+
const type = baseMimeType(file.type);
|
|
24
|
+
if (type === "image/svg+xml") return "type";
|
|
25
|
+
if (!rules.allowedMimeTypes.some((pattern) => mimeMatches(pattern, type))) return "type";
|
|
26
|
+
return file.size > rules.maxFileSize ? "size" : null;
|
|
27
|
+
}
|
|
28
|
+
/** What an upload handler may return: a URL, or a URL with the stored file's id. */
|
|
29
|
+
function normalizeRichTextUpload(result, rules = defaultRichTextImageRules) {
|
|
30
|
+
const upload = typeof result === "string" ? {
|
|
31
|
+
id: null,
|
|
32
|
+
src: result
|
|
33
|
+
} : {
|
|
34
|
+
id: String(result.id),
|
|
35
|
+
src: result.src
|
|
36
|
+
};
|
|
37
|
+
return isSafeImageSrc(upload.src, { allowDataImages: rules.allowBase64 }) ? upload : null;
|
|
38
|
+
}
|
|
39
|
+
/** `width` rounded and held in `min`..`max`. `max` wins when it is below `min`. */
|
|
40
|
+
function clampRichTextImageWidth(width, { min, max }) {
|
|
41
|
+
return Math.min(max, Math.max(min, Number.isNaN(width) ? min : Math.round(width)));
|
|
42
|
+
}
|
|
43
|
+
var EXTENSIONS = /* @__PURE__ */ new Map([
|
|
44
|
+
["jpeg", "jpg"],
|
|
45
|
+
["svg+xml", "svg"],
|
|
46
|
+
["x-icon", "ico"],
|
|
47
|
+
["vnd.microsoft.icon", "ico"]
|
|
48
|
+
]);
|
|
49
|
+
/** `<name>.<extension>` for downloading an image, from its alt text and mime type. */
|
|
50
|
+
function richTextImageFileName(alt, mimeType) {
|
|
51
|
+
const name = alt.replace(CONTROL, "").replace(/[/\\]/g, "").trim().replace(/^\.+/, "").trim();
|
|
52
|
+
const subtype = baseMimeType(mimeType).split("/")[1] ?? "";
|
|
53
|
+
const extension = EXTENSIONS.get(subtype) || subtype.replace(/[^a-z0-9]/g, "") || "png";
|
|
54
|
+
return `${name || "image"}.${extension}`;
|
|
55
|
+
}
|
|
56
|
+
function imageAction(id, defaultLabel) {
|
|
57
|
+
return Object.freeze({
|
|
58
|
+
id,
|
|
59
|
+
labelKey: `chelone.rich_text.image.${id}`,
|
|
60
|
+
defaultLabel
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/** The image popover's actions, in display order. */
|
|
64
|
+
var richTextImageActions = Object.freeze([
|
|
65
|
+
imageAction("view", "View image"),
|
|
66
|
+
imageAction("download", "Download image"),
|
|
67
|
+
imageAction("copyImage", "Copy image"),
|
|
68
|
+
imageAction("copyLink", "Copy link"),
|
|
69
|
+
imageAction("remove", "Remove image")
|
|
70
|
+
]);
|
|
71
|
+
var COLOR_NAMES = [
|
|
72
|
+
"red",
|
|
73
|
+
"orange",
|
|
74
|
+
"yellow",
|
|
75
|
+
"green",
|
|
76
|
+
"blue",
|
|
77
|
+
"purple",
|
|
78
|
+
"pink"
|
|
79
|
+
];
|
|
80
|
+
function color(id, palette, defaultLabel, value) {
|
|
81
|
+
return Object.freeze({
|
|
82
|
+
id,
|
|
83
|
+
palette,
|
|
84
|
+
labelKey: `chelone.rich_text.color.${id}`,
|
|
85
|
+
defaultLabel,
|
|
86
|
+
value
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function palette(name, label) {
|
|
90
|
+
return COLOR_NAMES.map((base) => {
|
|
91
|
+
const id = `${name}-${base}`;
|
|
92
|
+
return color(id, name, `${label} ${base}`, `var(--rich-text-color-${id})`);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The color picker's entries: `default` (clears the color), then three
|
|
97
|
+
* palettes of seven. The adapter's stylesheet defines each
|
|
98
|
+
* `--rich-text-color-<id>`.
|
|
99
|
+
*/
|
|
100
|
+
var richTextColors = Object.freeze([
|
|
101
|
+
color("default", "default", "Default", null),
|
|
102
|
+
...palette("bright", "Bright"),
|
|
103
|
+
...palette("soft", "Soft"),
|
|
104
|
+
...palette("muted", "Muted")
|
|
105
|
+
]);
|
|
106
|
+
var NUMBER = String.raw`[+-]?(?:\d+\.?\d*|\.\d+)%?`;
|
|
107
|
+
var SEPARATOR = String.raw`(?:\s*[,/]\s*|\s+)`;
|
|
108
|
+
var HEX_COLOR = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
109
|
+
var FUNCTION_COLOR = new RegExp(String.raw`^(?:rgba?|hsla?)\(\s*${NUMBER}(?:${SEPARATOR}${NUMBER}){2,3}\s*\)$`, "i");
|
|
110
|
+
var PALETTE_VALUES = new Set(richTextColors.flatMap((entry) => entry.value ? [entry.value] : []));
|
|
111
|
+
function isSafeColorValue(value) {
|
|
112
|
+
return PALETTE_VALUES.has(value) || HEX_COLOR.test(value) || FUNCTION_COLOR.test(value);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* `color: <value>` when `style` is one `color` declaration with a palette
|
|
116
|
+
* `var(...)`, hex, `rgb()`, `rgba()`, `hsl()` or `hsla()` value, else `null`.
|
|
117
|
+
* Everything else a style attribute can carry is dropped.
|
|
118
|
+
*/
|
|
119
|
+
function sanitizeRichTextStyle(style) {
|
|
120
|
+
const [declaration, ...rest] = style.split(";").map((declaration) => declaration.trim()).filter((declaration) => declaration !== "");
|
|
121
|
+
if (declaration === void 0 || rest.length > 0) return null;
|
|
122
|
+
const colon = declaration.indexOf(":");
|
|
123
|
+
if (colon === -1) return null;
|
|
124
|
+
if (declaration.slice(0, colon).trim().toLowerCase() !== "color") return null;
|
|
125
|
+
const value = declaration.slice(colon + 1).trim();
|
|
126
|
+
return isSafeColorValue(value) ? `color: ${value}` : null;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* `richTextSanitizeOptions` with `style` allowed through, for adapters that
|
|
130
|
+
* run `sanitizeRichTextStyle` on every `style` attribute.
|
|
131
|
+
*/
|
|
132
|
+
var richTextStyledSanitizeOptions = Object.freeze({
|
|
133
|
+
FORBID_TAGS: richTextSanitizeOptions.FORBID_TAGS,
|
|
134
|
+
FORBID_ATTR: Object.freeze(richTextSanitizeOptions.FORBID_ATTR.filter((attribute) => attribute !== "style"))
|
|
135
|
+
});
|
|
136
|
+
/** Options for tiptap's `CodeBlockLowlight.configure`, as shadmin sets them. */
|
|
137
|
+
var richTextCodeBlockOptions = Object.freeze({
|
|
138
|
+
languageClassPrefix: "language-",
|
|
139
|
+
defaultLanguage: null,
|
|
140
|
+
exitOnTripleEnter: true,
|
|
141
|
+
exitOnArrowDown: true,
|
|
142
|
+
enableTabIndentation: false,
|
|
143
|
+
tabSize: 4
|
|
144
|
+
});
|
|
145
|
+
function extraItem(id, group, defaultLabel) {
|
|
146
|
+
return Object.freeze({
|
|
147
|
+
id,
|
|
148
|
+
group,
|
|
149
|
+
labelKey: `chelone.rich_text.${id}`,
|
|
150
|
+
defaultLabel
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/** The toolbar buttons an image or color extension adds, shaped like `richTextToolbarItems`. */
|
|
154
|
+
var richTextExtraToolbarItems = Object.freeze([extraItem("image", "insert", "Image"), extraItem("color", "mark", "Text color")]);
|
|
155
|
+
/**
|
|
156
|
+
* The two ProseMirror plugin hooks that end a text color with its line.
|
|
157
|
+
* Splitting a block would carry the color into the next one, so after Enter
|
|
158
|
+
* the color's `textStyle` mark is dropped from the stored marks, unless colored
|
|
159
|
+
* text follows the cursor. An adapter wires them into
|
|
160
|
+
* `new Plugin({ props: { handleKeyDown }, appendTransaction })` and runs that
|
|
161
|
+
* plugin before the keymap, which handles Enter.
|
|
162
|
+
*/
|
|
163
|
+
function endColorOnEnter() {
|
|
164
|
+
let enterPressed = false;
|
|
165
|
+
return {
|
|
166
|
+
handleKeyDown(_view, event) {
|
|
167
|
+
enterPressed = event.key === "Enter" && !event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
168
|
+
return false;
|
|
169
|
+
},
|
|
170
|
+
appendTransaction(transactions, _previous, state) {
|
|
171
|
+
if (!enterPressed || !transactions.some((tr) => tr.docChanged)) return null;
|
|
172
|
+
enterPressed = false;
|
|
173
|
+
const { $from } = state.selection;
|
|
174
|
+
if (($from.nodeAfter?.marks ?? []).some((mark) => mark.type.name === "textStyle")) return null;
|
|
175
|
+
const marks = state.storedMarks ?? $from.marks();
|
|
176
|
+
const kept = marks.filter((mark) => mark.type.name !== "textStyle");
|
|
177
|
+
return kept.length === marks.length ? null : state.tr.setStoredMarks(kept);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
export { MAX_IMAGE_BYTES, checkRichTextImageFile, clampRichTextImageWidth, defaultRichTextImageRules, endColorOnEnter, normalizeRichTextUpload, richTextCodeBlockOptions, richTextColors, richTextExtraToolbarItems, richTextImageActions, richTextImageFileName, richTextStyledSanitizeOptions, sanitizeRichTextStyle };
|