@dineug/erd-editor 3.9.0 → 3.9.2

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/README.md CHANGED
@@ -160,6 +160,24 @@ erd-editor {
160
160
  | `change` | The document changed. Debounced, and never fired while `readonly`. Read `editor.value`. |
161
161
  | `changePresetTheme` | The theme was changed from inside the editor. `event.detail` carries the new options. |
162
162
 
163
+ ## Key bindings
164
+
165
+ `createKeyBindingMap()` returns the shortcuts an editor listens for until `setKeyBindingMap`
166
+ changes them, as a new map on every call. Each is a [tinykeys](https://github.com/jamiebuilds/tinykeys)
167
+ chord that names its key by `KeyboardEvent.code`, where `$mod` is Cmd on macOS and Ctrl elsewhere.
168
+
169
+ ```js
170
+ import { createKeyBindingMap } from '@dineug/erd-editor';
171
+
172
+ const shortcuts = Object.values(createKeyBindingMap())
173
+ .flat()
174
+ .map(({ shortcut }) => shortcut); // '$mod+KeyK', 'Alt+Enter', ...
175
+ ```
176
+
177
+ The editor listens on its own element, so a host that takes keys before the page does, with
178
+ global hotkeys or a capture-phase `keydown` listener, has to let these through while the editor
179
+ is focused. The `KeyBindingMap`, `KeyBindingName` and `ShortcutOption` types come with it.
180
+
163
181
  ## Syntax highlighting
164
182
 
165
183
  The SQL and code-generation panels are highlighted by [Shiki](https://shiki.style), in a shared
@@ -1,33 +1,34 @@
1
- import { $i as e, Ln as t, Xt as n, Z as r, _ as i, fn as a, h as o, n as s, r as c, t as l, u, x as d, xi as f } from "../schemaGCService-CFiz1xOG.js";
1
+ import { Ii as e, Xn as t, c as n, et as r, h as i, nn as a, p as o, t as s, va as c, wn as l, y as u } from "../store-hooks-DuzDVhPy.js";
2
+ import { n as d, t as f } from "../schemaGCService-BagQ1wfF.js";
2
3
  import { isEmpty as p } from "es-toolkit/compat";
3
4
  import { omit as m } from "es-toolkit";
4
5
  import { Observable as h, Subject as g, debounceTime as _, map as v } from "rxjs";
5
6
  //#region src/engine/replication-store.ts
6
7
  var y = { change: "change" };
7
8
  function b(b) {
8
- let x = /* @__PURE__ */ new Set(), S = i(b), C = u(S, !1);
9
- C.dispatchSync(a({
9
+ let x = /* @__PURE__ */ new Set(), S = i(b), C = n(S, !1);
10
+ C.dispatchSync(l({
10
11
  width: 0,
11
12
  height: 0
12
13
  }));
13
- let w = c(C), T = new g(), E = new h((e) => C.subscribe((t) => e.next(t))).pipe(o(d), _(200)), D = /* @__PURE__ */ new Set(), O = new l(), k = (e) => (D.has(e) || D.add(e), () => {
14
+ let w = s(C), T = new g(), E = new h((e) => C.subscribe((t) => e.next(t))).pipe(o(u), _(200)), D = /* @__PURE__ */ new Set(), O = new f(), k = (e) => (D.has(e) || D.add(e), () => {
14
15
  D.delete(e);
15
16
  }), A = (e, t) => {
16
- D.forEach((r) => {
17
- let i = Reflect.get(r, e);
18
- n(i, t);
17
+ D.forEach((n) => {
18
+ let r = Reflect.get(n, e);
19
+ a(r, t);
19
20
  });
20
21
  }, j = (n) => {
21
- let i = f(n);
22
- C.dispatchSync(r(p(i) ? "{}" : i)), O.run(e(C.state)).then((e) => {
23
- (e.tableIds.length || e.tableColumnIds.length || e.relationshipIds.length || e.indexIds.length || e.indexColumnIds.length || e.memoIds.length) && (s(C.state, e), C.dispatchSync(t()));
22
+ let i = e(n);
23
+ C.dispatchSync(r(p(i) ? "{}" : i)), O.run(c(C.state)).then((e) => {
24
+ (e.tableIds.length || e.tableColumnIds.length || e.relationshipIds.length || e.indexIds.length || e.indexColumnIds.length || e.memoIds.length) && (d(C.state, e), C.dispatchSync(t()));
24
25
  });
25
26
  }, M = (e) => {
26
27
  T.next([e].flat());
27
28
  };
28
- return x.add(E.subscribe(() => A(y.change, void 0))).add(T.pipe(o(d), v((e) => e.map((e) => m(e, ["tags"])))).subscribe(C.dispatchSync)), Object.freeze({
29
+ return x.add(E.subscribe(() => A(y.change, void 0))).add(T.pipe(o(u), v((e) => e.map((e) => m(e, ["tags"])))).subscribe(C.dispatchSync)), Object.freeze({
29
30
  get value() {
30
- return e(C.state);
31
+ return c(C.state);
31
32
  },
32
33
  on: k,
33
34
  setInitialValue: j,
@@ -0,0 +1,9 @@
1
+ import { AnyAction } from '@dineug/r-html';
2
+ import { Column } from '../../../internal-types';
3
+ /**
4
+ * Adds a NOT NULL copy of each start column to the end table under the id at
5
+ * the same index of endColumnIds, carrying its name, data type, default and
6
+ * comment. The caller owns the ids because the relationship needs them too.
7
+ */
8
+ export declare const toForeignKeyActions: (startColumns: Column[], endTableId: string, endColumnIds: string[]) => AnyAction[];
9
+ //# sourceMappingURL=fkColumns.d.ts.map
@@ -1,2 +1,11 @@
1
- export declare const actions$: {};
1
+ import { GeneratorAction } from '../../generator.actions';
2
+ /**
3
+ * Draws a relationship between two live tables in one batch, giving the start
4
+ * table a primary key first when it has none and copying the keys onto the end
5
+ * table, which may be the start table itself. An unknown table yields nothing.
6
+ */
7
+ export declare const addRelationshipAction$: (startTableId: string, endTableId: string, relationshipType: number) => GeneratorAction;
8
+ export declare const actions$: {
9
+ addRelationshipAction$: (startTableId: string, endTableId: string, relationshipType: number) => GeneratorAction;
10
+ };
2
11
  //# sourceMappingURL=generator.actions.d.ts.map
@@ -4,10 +4,17 @@ export declare const addTableAction$: () => GeneratorAction;
4
4
  export declare const removeTableAction$: (id?: string) => GeneratorAction;
5
5
  export declare const selectTableAction$: (id: string, $mod: boolean) => GeneratorAction;
6
6
  export declare const pasteTableAction$: (columns: Column[]) => GeneratorAction;
7
+ /**
8
+ * Runs the engine's sort once, on copies of the live tables, and places each
9
+ * table at the point it found. A replayed table.sort measures each replica's
10
+ * own text, so only the placed points come out the same everywhere.
11
+ */
12
+ export declare const sortTablesToMoveAction$: () => GeneratorAction;
7
13
  export declare const actions$: {
8
14
  addTableAction$: () => GeneratorAction;
9
15
  removeTableAction$: (id?: string) => GeneratorAction;
10
16
  selectTableAction$: (id: string, $mod: boolean) => GeneratorAction;
11
17
  pasteTableAction$: (columns: Column[]) => GeneratorAction;
18
+ sortTablesToMoveAction$: () => GeneratorAction;
12
19
  };
13
20
  //# sourceMappingURL=generator.actions.d.ts.map
@@ -0,0 +1,93 @@
1
+ import { AnyAction, CompositionActions } from '@dineug/r-html';
2
+ import { FocusType } from './modules/editor/state';
3
+ import { RootState } from './state';
4
+ import { Unsubscribe, ValuesType } from '../internal-types';
5
+ export type PeerStoreOptions = {
6
+ nickname: string;
7
+ toWidth?: (text: string) => number;
8
+ /** Sends the focused cell to the other peers. A headless session turns it off. */
9
+ presence?: boolean;
10
+ /** Refuses every dispatch, undo and redo, as a readonly document does. */
11
+ readonly?: boolean;
12
+ };
13
+ /** The cell to focus before an edit, so the others see where the peer works. */
14
+ export type DispatchFocus = {
15
+ tableId: string;
16
+ columnId?: string;
17
+ focusType: FocusType;
18
+ };
19
+ export type DispatchOptions = {
20
+ /** Names the dispatch as one undo unit, echoed back by undo and redo. */
21
+ label?: string;
22
+ focus?: DispatchFocus;
23
+ };
24
+ export type DispatchReport = {
25
+ label: string | null;
26
+ /** The document changes the dispatch made, version stamped, in order. */
27
+ actions: AnyAction[];
28
+ createdIds: string[];
29
+ /** Outbound batches that carried a document change. */
30
+ batches: number;
31
+ /** Undo entries the history took in. */
32
+ historyEntries: number;
33
+ };
34
+ /**
35
+ * What an undo or redo reverted: the dispatch's label, how many history
36
+ * entries it took, and the labels passed over because the engine made them
37
+ * no undo entry. Zero entries means there was nothing left to revert.
38
+ */
39
+ export type RevertResult = {
40
+ label: string | null;
41
+ entries: number;
42
+ skipped: string[];
43
+ };
44
+ /** Why a peer store refused a call before dispatching anything. */
45
+ export declare const PeerStoreErrorCode: {
46
+ readonly readonly: "readonly";
47
+ readonly destroyed: "destroyed";
48
+ };
49
+ export type PeerStoreErrorCode = ValuesType<typeof PeerStoreErrorCode>;
50
+ /** A refusal: the code says why, operation names the call, and nothing moved. */
51
+ export declare class PeerStoreError extends Error {
52
+ readonly code: PeerStoreErrorCode;
53
+ readonly operation: string;
54
+ constructor(code: PeerStoreErrorCode, operation: string);
55
+ }
56
+ export type PeerStore = {
57
+ readonly editorId: string;
58
+ /** The document as the element would save it. */
59
+ readonly value: string;
60
+ /** The live state, for readers that serialize it another way. */
61
+ readonly state: RootState;
62
+ readonly isReadonly: boolean;
63
+ readonly isDestroyed: boolean;
64
+ setReadonly: (readonly: boolean) => void;
65
+ /**
66
+ * Replaces the document and forgets every undo entry and label with it,
67
+ * since those were taken against the old one. The one reseed operation.
68
+ */
69
+ setInitialValue: (value: string) => void;
70
+ mergeClock: (version: number) => void;
71
+ dispatch: (actions: CompositionActions, options?: DispatchOptions) => DispatchReport;
72
+ /** Applies another peer's batch now, so a read right after sees it. */
73
+ receive: (actions: AnyAction[] | AnyAction) => void;
74
+ /**
75
+ * Runs dispatches as one undo unit under label, so one undo reverts every
76
+ * entry they made. A group inside another joins it; undo, redo and a reseed
77
+ * are refused inside one, since the unit would no longer match the history.
78
+ */
79
+ group: <T>(label: string, run: () => T) => T;
80
+ undo: () => RevertResult;
81
+ redo: () => RevertResult;
82
+ subscribe: (fn: (actions: AnyAction[]) => void) => Unsubscribe;
83
+ /** Closes the history's stream groups, then the outbound pipe's. */
84
+ flushStreamBuffers: () => void;
85
+ destroy: () => void;
86
+ };
87
+ /**
88
+ * A collaborating editor with no screen: the element's store pipeline, driven
89
+ * by dispatches instead of a pointer. Stream buffers close when a dispatch
90
+ * ends rather than after a quiet period, so one dispatch is one outbound batch.
91
+ */
92
+ export declare function createPeerStore({ nickname, toWidth, presence, readonly, }: PeerStoreOptions): PeerStore;
93
+ //# sourceMappingURL=peer-store.d.ts.map
@@ -0,0 +1,23 @@
1
+ import { Editor } from './modules/editor/state';
2
+ import { RxStore } from './rx-store';
3
+ /** A third of the expiry, so two beats can go missing before a peer drops the cell. */
4
+ export declare const FOCUS_HEARTBEAT_MS: number;
5
+ export type FocusPresence = {
6
+ destroy: () => void;
7
+ };
8
+ declare const SHARED_TRACKER_MAPS: readonly ["sharedMouseTrackerMap", "sharedFocusTrackerMap", "sharedSelectionTrackerMap", "sharedDragSelectTrackerMap"];
9
+ type SharedTrackerMaps = Pick<Editor, (typeof SHARED_TRACKER_MAPS)[number]>;
10
+ /**
11
+ * Clears the expiry the engine set on every tracker another peer sent, and the
12
+ * trackers with it. A pending expiry is a Node timer the reducer never releases,
13
+ * so a closed peer's process would wait up to 90 seconds for it.
14
+ */
15
+ export declare function clearSharedTrackers(editor: SharedTrackerMaps): void;
16
+ /**
17
+ * The focus half of the element's presence tracker: the focused cell goes out
18
+ * when it changes, again on every heartbeat and whenever a peer joins. Mouse,
19
+ * selection and drag selection are never sent.
20
+ */
21
+ export declare function createFocusPresence(store: Pick<RxStore, 'state' | 'subscribe' | 'dispatch'>): FocusPresence;
22
+ export {};
23
+ //# sourceMappingURL=presence.d.ts.map
@@ -1,4 +1,9 @@
1
1
  import { AnyAction } from '@dineug/r-html';
2
2
  import { Observable } from 'rxjs';
3
+ /**
4
+ * Holds the source's batches while the circuit is open, flushes them when it
5
+ * closes and lets batches straight through until it opens again. The source is
6
+ * subscribed once, so a cold upstream's timers cannot flush ahead of its value.
7
+ */
3
8
  export declare const bufferCircuitBreaker: (openingNotifier$: Observable<any>, closingNotifier$: Observable<any>) => (source$: Observable<Array<AnyAction>>) => Observable<AnyAction[]>;
4
9
  //# sourceMappingURL=bufferCircuitBreaker.d.ts.map
@@ -0,0 +1,10 @@
1
+ import { AnyAction } from '@dineug/r-html';
2
+ import { Observable } from 'rxjs';
3
+ import { StreamBufferOperator } from './groupByStreamActions';
4
+ /**
5
+ * Builds the outbound compressor around what closes an edit stream's group,
6
+ * left to groupByStreamActions' 200 ms quiet period when omitted. Presence
7
+ * keeps its own 100 ms throttle either way.
8
+ */
9
+ export declare const createSharedStreamActionsCompressor: (streamBufferOperator?: StreamBufferOperator) => (source$: Observable<Array<AnyAction>>) => Observable<AnyAction[]>;
10
+ //# sourceMappingURL=createSharedStreamActionsCompressor.d.ts.map
@@ -0,0 +1,15 @@
1
+ import { Observable } from 'rxjs';
2
+ import { StreamBufferOperator } from './groupByStreamActions';
3
+ /**
4
+ * Closes a stream buffer when the notifier fires instead of after a quiet
5
+ * period. A group arms only once it holds a value, so a tick with nothing
6
+ * pending emits nothing and an extra tick is harmless.
7
+ */
8
+ export declare const flushOnNotifier: (notifier$: Observable<unknown>) => StreamBufferOperator;
9
+ /**
10
+ * Closes a stream buffer after groupByStreamActions' 200 ms quiet period, or
11
+ * at once when the notifier fires: the editor keeps its timing, and a host
12
+ * can still send what is held before it lets the store go.
13
+ */
14
+ export declare const quietPeriodOrNotifier: (notifier$: Observable<unknown>, quietMs?: number) => StreamBufferOperator;
15
+ //# sourceMappingURL=flushOnNotifier.d.ts.map
@@ -1,6 +1,8 @@
1
1
  import { AnyAction } from '@dineug/r-html';
2
2
  import { MonoTypeOperatorFunction, Observable } from 'rxjs';
3
3
  type Regroup = [string, Array<string> | ReadonlyArray<string>];
4
- export declare const groupByStreamActions: (streamActionTypes: Array<string> | ReadonlyArray<string>, regroups?: Regroup[], bufferClosingNotifierOperator?: MonoTypeOperatorFunction<Array<AnyAction>>) => (source$: Observable<Array<AnyAction>>) => Observable<AnyAction[]>;
4
+ /** Runs on a stream group's batches; each value it emits closes the buffer. */
5
+ export type StreamBufferOperator = MonoTypeOperatorFunction<Array<AnyAction>>;
6
+ export declare const groupByStreamActions: (streamActionTypes: Array<string> | ReadonlyArray<string>, regroups?: Regroup[], bufferClosingNotifierOperator?: StreamBufferOperator) => (source$: Observable<Array<AnyAction>>) => Observable<AnyAction[]>;
5
7
  export {};
6
8
  //# sourceMappingURL=groupByStreamActions.d.ts.map
@@ -1,4 +1,3 @@
1
- import { AnyAction } from '@dineug/r-html';
2
- import { Observable } from 'rxjs';
3
- export declare const sharedStreamActionsCompressor: (source$: Observable<Array<AnyAction>>) => Observable<AnyAction[]>;
1
+ /** The editor's compressor: a 200 ms quiet period closes each stream group. */
2
+ export declare const sharedStreamActionsCompressor: (source$: import('rxjs').Observable<Array<import('@dineug/r-html').AnyAction>>) => import('rxjs').Observable<import('@dineug/r-html').AnyAction[]>;
4
3
  //# sourceMappingURL=sharedStreamActionsCompressor.d.ts.map
@@ -9,11 +9,23 @@ export type RxStore = Store & {
9
9
  history: History;
10
10
  resetHistory: () => void;
11
11
  change$: Observable<Array<AnyAction>>;
12
+ /**
13
+ * Closes the stream groups the history is still buffering, now. A no-op
14
+ * unless the store was created with manualStreamFlush.
15
+ */
16
+ flushStreamBuffers: () => void;
12
17
  };
13
18
  export type RxStoreOptions = {
14
19
  getReadonly?: () => boolean;
15
20
  getHistory?: (options: HistoryOptions) => History;
21
+ /** Closes stream buffers on flushStreamBuffers() instead of after 200 ms. */
22
+ manualStreamFlush?: boolean;
23
+ /**
24
+ * Keeps the state a plain object when false. Nothing renders from a
25
+ * headless store, and the reactive proxy reads DOM globals Node lacks.
26
+ */
27
+ observable?: boolean;
16
28
  };
17
29
  export declare const HISTORY_LIMIT = 2048;
18
- export declare function createRxStore(context: EngineContext, { getReadonly, getHistory }?: RxStoreOptions): RxStore;
30
+ export declare function createRxStore(context: EngineContext, { getReadonly, getHistory, manualStreamFlush, observable, }?: RxStoreOptions): RxStore;
19
31
  //# sourceMappingURL=rx-store.d.ts.map
@@ -7,10 +7,20 @@ export type SharedStore = {
7
7
  dispatch: (actions: Array<AnyAction> | AnyAction) => void;
8
8
  dispatchSync: (actions: Array<AnyAction> | AnyAction) => void;
9
9
  subscribe: (fn: (value: AnyAction[]) => void) => Unsubscribe;
10
+ /**
11
+ * Sends the stream groups still held back from the peers, now, rather than
12
+ * after their quiet period: what a host calls before it lets a store go.
13
+ */
14
+ flushStreamBuffers: () => void;
10
15
  destroy: () => void;
11
16
  };
12
17
  export type SharedStoreConfig = {
13
18
  getNickname?: () => string;
14
19
  };
15
- export declare function createSharedStore(store: RxStore, config?: SharedStoreConfig): SharedStore;
20
+ /** Not part of getSharedStore's config: only a headless peer sets this. */
21
+ export type SharedStoreInternalOptions = {
22
+ /** Closes stream buffers on flushStreamBuffers() instead of after 200 ms. */
23
+ manualStreamFlush?: boolean;
24
+ };
25
+ export declare function createSharedStore(store: RxStore, config?: SharedStoreConfig, internal?: SharedStoreInternalOptions): SharedStore;
16
26
  //# sourceMappingURL=shared-store.d.ts.map
@@ -0,0 +1,12 @@
1
+ import { RxStore } from './rx-store';
2
+ import { SharedStore } from './shared-store';
3
+ export type StreamFlusher = {
4
+ /** History first, then the outbound pipe, so a batch never precedes its undo entry. */
5
+ flush: () => void;
6
+ };
7
+ /**
8
+ * Closes both stores' stream buffers now. Each store knows how many ticks its
9
+ * own pipe needs, so this knows neither the tick count nor any rxjs type.
10
+ */
11
+ export declare function createStreamFlusher(rxStore: Pick<RxStore, 'flushStreamBuffers'>, sharedStore: Pick<SharedStore, 'flushStreamBuffers'>): StreamFlusher;
12
+ //# sourceMappingURL=stream-flush.d.ts.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The text width a peer measures with no canvas, the same estimate the
3
+ * replica worker falls back to. A reload recalculates every width, so an
4
+ * estimate written by a headless edit never outlives the next open.
5
+ */
6
+ export declare const defaultToWidth: (text: string) => number;
7
+ //# sourceMappingURL=to-width.d.ts.map