svedit 0.11.1 → 0.13.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.
Files changed (93) hide show
  1. package/README.md +260 -82
  2. package/dist/Command.svelte.d.ts +54 -29
  3. package/dist/Command.svelte.d.ts.map +1 -1
  4. package/dist/Command.svelte.js +286 -283
  5. package/dist/CustomProperty.svelte +3 -5
  6. package/dist/CustomProperty.svelte.d.ts +3 -6
  7. package/dist/CustomProperty.svelte.d.ts.map +1 -1
  8. package/dist/KeyMapper.svelte.d.ts +19 -6
  9. package/dist/KeyMapper.svelte.d.ts.map +1 -1
  10. package/dist/KeyMapper.svelte.js +95 -112
  11. package/dist/Node.svelte +40 -9
  12. package/dist/Node.svelte.d.ts +3 -5
  13. package/dist/Node.svelte.d.ts.map +1 -1
  14. package/dist/NodeArrayProperty.svelte +142 -18
  15. package/dist/NodeArrayProperty.svelte.d.ts +3 -5
  16. package/dist/NodeArrayProperty.svelte.d.ts.map +1 -1
  17. package/dist/NodeCaret.svelte +1 -1
  18. package/dist/NodeCaret.svelte.d.ts +6 -14
  19. package/dist/NodeCaret.svelte.d.ts.map +1 -1
  20. package/dist/NodeGap.svelte +60 -12
  21. package/dist/NodeGap.svelte.d.ts +7 -14
  22. package/dist/NodeGap.svelte.d.ts.map +1 -1
  23. package/dist/NodeGapMarkers.svelte +19 -10
  24. package/dist/NodeGapMarkers.svelte.d.ts +6 -11
  25. package/dist/NodeGapMarkers.svelte.d.ts.map +1 -1
  26. package/dist/NodeSelectionMarkers.svelte +20 -6
  27. package/dist/NodeSelectionMarkers.svelte.d.ts +2 -5
  28. package/dist/NodeSelectionMarkers.svelte.d.ts.map +1 -1
  29. package/dist/Session.svelte.d.ts +85 -116
  30. package/dist/Session.svelte.d.ts.map +1 -1
  31. package/dist/Session.svelte.js +543 -571
  32. package/dist/Svedit.svelte +243 -216
  33. package/dist/Svedit.svelte.d.ts +30 -10
  34. package/dist/Svedit.svelte.d.ts.map +1 -1
  35. package/dist/{AnnotatedTextProperty.svelte → TextProperty.svelte} +71 -56
  36. package/dist/TextProperty.svelte.d.ts +5 -0
  37. package/dist/TextProperty.svelte.d.ts.map +1 -0
  38. package/dist/Transaction.svelte.d.ts +101 -126
  39. package/dist/Transaction.svelte.d.ts.map +1 -1
  40. package/dist/Transaction.svelte.js +843 -827
  41. package/dist/UnknownNode.svelte +4 -3
  42. package/dist/UnknownNode.svelte.d.ts +5 -9
  43. package/dist/UnknownNode.svelte.d.ts.map +1 -1
  44. package/dist/doc_utils.d.ts +109 -122
  45. package/dist/doc_utils.d.ts.map +1 -1
  46. package/dist/doc_utils.js +726 -698
  47. package/dist/index.d.ts +20 -17
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +2 -18
  50. package/dist/node_visibility.svelte.d.ts +132 -5
  51. package/dist/node_visibility.svelte.d.ts.map +1 -1
  52. package/dist/node_visibility.svelte.js +535 -642
  53. package/dist/paste_utils.d.ts +11 -43
  54. package/dist/paste_utils.d.ts.map +1 -1
  55. package/dist/paste_utils.js +111 -149
  56. package/dist/transforms.svelte.d.ts +9 -7
  57. package/dist/transforms.svelte.d.ts.map +1 -1
  58. package/dist/transforms.svelte.js +126 -152
  59. package/dist/types.d.ts +329 -291
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/types.js +1 -0
  62. package/dist/utils.d.ts +79 -95
  63. package/dist/utils.d.ts.map +1 -1
  64. package/dist/utils.js +392 -305
  65. package/package.json +69 -67
  66. package/src/lib/{Command.svelte.js → Command.svelte.ts} +111 -41
  67. package/src/lib/CustomProperty.svelte +3 -5
  68. package/src/lib/{KeyMapper.svelte.js → KeyMapper.svelte.ts} +28 -11
  69. package/src/lib/Node.svelte +40 -9
  70. package/src/lib/NodeArrayProperty.svelte +142 -18
  71. package/src/lib/NodeCaret.svelte +1 -1
  72. package/src/lib/NodeGap.svelte +60 -12
  73. package/src/lib/NodeGapMarkers.svelte +19 -10
  74. package/src/lib/NodeSelectionMarkers.svelte +20 -6
  75. package/src/lib/Session.svelte.ts +656 -0
  76. package/src/lib/Svedit.svelte +243 -216
  77. package/src/lib/{AnnotatedTextProperty.svelte → TextProperty.svelte} +71 -56
  78. package/src/lib/Transaction.svelte.ts +1020 -0
  79. package/src/lib/UnknownNode.svelte +4 -3
  80. package/src/lib/doc_utils.ts +1120 -0
  81. package/src/lib/{index.js → index.ts} +48 -1
  82. package/src/lib/node_visibility.svelte.ts +658 -0
  83. package/src/lib/{paste_utils.js → paste_utils.ts} +51 -59
  84. package/src/lib/{transforms.svelte.js → transforms.svelte.ts} +44 -27
  85. package/src/lib/{types.d.ts → types.ts} +250 -103
  86. package/src/lib/utils.ts +660 -0
  87. package/dist/AnnotatedTextProperty.svelte.d.ts +0 -7
  88. package/dist/AnnotatedTextProperty.svelte.d.ts.map +0 -1
  89. package/src/lib/Session.svelte.js +0 -575
  90. package/src/lib/Transaction.svelte.js +0 -846
  91. package/src/lib/doc_utils.js +0 -807
  92. package/src/lib/node_visibility.svelte.js +0 -718
  93. package/src/lib/utils.js +0 -465
@@ -1 +1 @@
1
- {"version":3,"file":"NodeCaret.svelte.d.ts","sourceRoot":"","sources":["../src/lib/NodeCaret.svelte.js"],"names":[],"mappings":";;;;;;;;AA4BA;;;;mBAAqH;6CATxE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,OAAO,OAAO,QAAQ;IAC3L,cAAc,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,WAAW,OAAO,SAAS;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"NodeCaret.svelte.d.ts","sourceRoot":"","sources":["../src/lib/NodeCaret.svelte.ts"],"names":[],"mappings":"AAmBA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,SAAS;;kBAA+E,CAAC;AAC7E,KAAK,SAAS,GAAG,YAAY,CAAC,OAAO,SAAS,CAAC,CAAC;AAClD,eAAe,SAAS,CAAC"}
@@ -1,6 +1,6 @@
1
- <script module>
2
- function cycle_z_index(/** @type {MouseEvent} */ e) {
3
- const el = /** @type {HTMLElement} */ (e.currentTarget);
1
+ <script module lang="ts">
2
+ function cycle_z_index(e: MouseEvent) {
3
+ const el = e.currentTarget as HTMLElement;
4
4
  if (el.hasAttribute('data-sent-to-back')) {
5
5
  document.querySelectorAll('.svedit-selectable[data-sent-to-back]').forEach((prev) => {
6
6
  prev.removeAttribute('data-sent-to-back');
@@ -10,9 +10,11 @@
10
10
  }
11
11
  </script>
12
12
 
13
- <script>
13
+ <script lang="ts">
14
14
  import { serialize_path } from './utils.js';
15
15
  import { getContext } from 'svelte';
16
+ import { should_position_gap } from './node_visibility.svelte.js';
17
+ import type { DocumentPath, SveditRenderContext } from './types.js';
16
18
 
17
19
  /**
18
20
  * ┌─────────────────────────────────────────────────────────────────┐
@@ -37,10 +39,13 @@
37
39
  *
38
40
  * Always present in the DOM when editable (stable structure for
39
41
  * selection anchoring, scrollTo, etc.). Anchor positioning is
40
- * activated lazily via the `.positioned` class, toggled imperatively
41
- * by the IO callback / MO / bootstrap in node_visibility — keeps
42
- * NodeGap allocation-free of per-instance Svelte signals, which
43
- * matters at 1000+ NodeGaps per scroll frame.
42
+ * activated lazily via the `.positioned` class, derived reactively
43
+ * from the visibility registry's array_indices / edge_map. Svelte's
44
+ * reactive collections track reads at per-key granularity, so a
45
+ * registry write only
46
+ * re-evaluates the adjacent gaps. Applying the class declaratively
47
+ * (instead of imperative classList toggles) means it survives DOM
48
+ * recreation without a document change (e.g. dev-mode HMR).
44
49
  *
45
50
  * `position-visibility: anchors-visible` does NOT skip anchor
46
51
  * resolution — the browser resolves all anchor() then hides the
@@ -54,14 +59,35 @@
54
59
  * sure your custom component also handles read-only mode by rendering a
55
60
  * plain `.node-gap` placeholder without editable internals.
56
61
  */
57
- const svedit = getContext('svedit');
58
- let { array_path, offset, count, empty = false } = $props();
62
+ const svedit = getContext<SveditRenderContext>('svedit');
63
+ let {
64
+ array_path,
65
+ offset,
66
+ count,
67
+ empty = false
68
+ }: { array_path: DocumentPath; offset: number; count: number; empty?: boolean } = $props();
59
69
  let is_editable = $derived(svedit.editable);
60
70
 
61
71
  let is_first = $derived(offset === 0);
62
72
  let is_last = $derived(offset === count);
63
73
  let type = $derived(is_first ? 'gap-before' : 'gap-after');
64
74
 
75
+ let path_str = $derived(serialize_path(array_path));
76
+ // Two-derived split is load-bearing: get_array_indices lazily
77
+ // creates the set, and Svelte doesn't track deps on state created
78
+ // inside the reading derived — so acquire here, read in `positioned`.
79
+ let near_indices = $derived(svedit.visibility_registry.get_array_indices(path_str));
80
+ let positioned = $derived(
81
+ is_editable &&
82
+ should_position_gap(
83
+ near_indices,
84
+ svedit.visibility_registry.edge_map.get(path_str),
85
+ offset,
86
+ is_last,
87
+ empty
88
+ )
89
+ );
90
+
65
91
  let gap_style = $derived.by(() => {
66
92
  if (!is_editable) return '';
67
93
  const prev_idx = offset - 1;
@@ -87,8 +113,9 @@
87
113
  class:gap-after={!is_first}
88
114
  class:empty
89
115
  class:last={is_last}
116
+ class:positioned
90
117
  data-type={type}
91
- data-gap-array-path={serialize_path(array_path)}
118
+ data-gap-array-path={path_str}
92
119
  data-gap-offset={offset}
93
120
  style={gap_style}
94
121
  >
@@ -111,9 +138,30 @@
111
138
  }
112
139
 
113
140
  /* ------------------------------------------------------------------ */
114
- /* Un-positioned: minimal in-DOM presence, no anchor resolution */
141
+ /* Un-positioned: no layout box at all */
115
142
  /* ------------------------------------------------------------------ */
116
143
 
144
+ /*
145
+ * display: none is load-bearing for large documents. A zero-size
146
+ * absolutely-positioned selectable still belongs to the containing
147
+ * block's out-of-flow list, and the browser lays out EVERY such box
148
+ * on EVERY layout pass — measured ~430ms per pass at 2000 nodes
149
+ * (~3500 gaps) in Chrome vs ~23ms with the boxes removed. That cost
150
+ * hits every keystroke (the per-change reconcile reads a rect for
151
+ * every node, and selection rendering forces layout), every window
152
+ * resize frame, and every scroll-triggered layout.
153
+ *
154
+ * Off-screen gaps therefore contribute no layout box. DOM structure
155
+ * stays stable (the .node-gap wrapper and selectable elements remain),
156
+ * and DOM Ranges may still point into display:none elements, so
157
+ * programmatic node selections targeting off-screen gaps keep working.
158
+ * Empty-array gaps are excluded: they must stay clickable/visible even
159
+ * before reconcile positions them.
160
+ */
161
+ :global(.node-gap:not(.positioned):not(.empty) .svedit-selectable) {
162
+ display: none;
163
+ }
164
+
117
165
  :global(.node-gap:not(.positioned) .svedit-selectable) {
118
166
  position: absolute;
119
167
  pointer-events: none;
@@ -1,18 +1,11 @@
1
- export default NodeGap;
2
- type NodeGap = {
3
- $on?(type: string, callback: (e: any) => void): () => void;
4
- $set?(props: Partial<$$ComponentProps>): void;
5
- };
6
- declare const NodeGap: import("svelte").Component<{
7
- array_path: any;
8
- offset: any;
9
- count: any;
10
- empty?: boolean;
11
- }, {}, "">;
1
+ import type { DocumentPath } from './types.js';
12
2
  type $$ComponentProps = {
13
- array_path: any;
14
- offset: any;
15
- count: any;
3
+ array_path: DocumentPath;
4
+ offset: number;
5
+ count: number;
16
6
  empty?: boolean;
17
7
  };
8
+ declare const NodeGap: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type NodeGap = ReturnType<typeof NodeGap>;
10
+ export default NodeGap;
18
11
  //# sourceMappingURL=NodeGap.svelte.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NodeGap.svelte.d.ts","sourceRoot":"","sources":["../src/lib/NodeGap.svelte.js"],"names":[],"mappings":";;;;;AAwGA;gBA1CgC,GAAG;YAAU,GAAG;WAAS,GAAG;YAAU,OAAO;WA0CvB;wBA1CpC;IAAE,UAAU,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE"}
1
+ {"version":3,"file":"NodeGap.svelte.d.ts","sourceRoot":"","sources":["../src/lib/NodeGap.svelte.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,YAAY,EAAuB,MAAM,YAAY,CAAC;AAEnE,KAAK,gBAAgB,GAAI;IAAE,UAAU,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAkHvG,QAAA,MAAM,OAAO,sDAAwC,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
@@ -1,7 +1,8 @@
1
- <script>
1
+ <script lang="ts">
2
2
  import { getContext } from 'svelte';
3
3
  import NodeCaret from './NodeCaret.svelte';
4
4
  import { PATH_SEPARATOR, serialize_path } from './utils.js';
5
+ import type { DocumentPath, SveditRenderContext } from './types.js';
5
6
 
6
7
  /**
7
8
  * ┌─────────────────────────────────────────────────────────────────┐
@@ -28,9 +29,9 @@
28
29
  * re-evaluates when THIS array's visible-index set changes.
29
30
  */
30
31
 
31
- let { path, count } = $props();
32
+ let { path, count }: { path: DocumentPath; count: number } = $props();
32
33
 
33
- const svedit = getContext('svedit');
34
+ const svedit = getContext<SveditRenderContext>('svedit');
34
35
  let path_str = $derived(serialize_path(path));
35
36
  let visible = $derived(svedit.visibility_registry.get_array_indices(path_str));
36
37
 
@@ -51,8 +52,16 @@
51
52
  ? `;--_f:${anchor_prefix}${PATH_SEPARATOR}0;--_s:${anchor_prefix}${PATH_SEPARATOR}1`
52
53
  : '';
53
54
 
54
- /** @type {Array<{key:string,offset:number,type:string,vars:string,is_first:boolean,is_last:boolean,has_pair:boolean}>} */
55
- const result = [];
55
+ type GapMarker = {
56
+ key: string;
57
+ offset: number;
58
+ type: string;
59
+ vars: string;
60
+ is_first: boolean;
61
+ is_last: boolean;
62
+ has_pair: boolean;
63
+ };
64
+ const result: GapMarker[] = [];
56
65
 
57
66
  if (count === 0) {
58
67
  if (visible.has(0)) {
@@ -73,15 +82,15 @@
73
82
  // 1000 nodes with ~30 visible, this drops 1001 SvelteSet/SvelteMap
74
83
  // reads per re-derivation to ~30. Filter stale indices >= count
75
84
  // that may briefly remain after a node delete (until MO fires).
76
- const sorted = [];
85
+ const sorted: number[] = [];
77
86
  for (const i of visible) {
78
87
  if (i >= 0 && i < count) sorted.push(i);
79
88
  }
80
89
  sorted.sort((a, b) => a - b);
81
90
  if (sorted.length === 0) return result;
82
91
 
83
- const offsets = [];
84
- function add_offset(offset) {
92
+ const offsets: number[] = [];
93
+ function add_offset(offset: number) {
85
94
  if (!offsets.includes(offset)) offsets.push(offset);
86
95
  }
87
96
 
@@ -112,7 +121,7 @@
112
121
  ? `${g_prefix}${PATH_SEPARATOR}0-gap-before`
113
122
  : `${g_prefix}${PATH_SEPARATOR}${offset - 1}-gap-after`;
114
123
 
115
- let type, vars;
124
+ let type: string, vars: string;
116
125
  if (is_first || is_last) {
117
126
  type = 'gap-edge';
118
127
  const adjacent = is_first
@@ -646,4 +655,4 @@
646
655
  outline: 1px solid blue;
647
656
  outline-offset: 0.5px;
648
657
  } */
649
- </style>
658
+ </style>
@@ -1,14 +1,9 @@
1
- export default NodeGapMarkers;
2
- type NodeGapMarkers = {
3
- $on?(type: string, callback: (e: any) => void): () => void;
4
- $set?(props: Partial<$$ComponentProps>): void;
5
- };
6
- declare const NodeGapMarkers: import("svelte").Component<{
7
- path: any;
8
- count: any;
9
- }, {}, "">;
1
+ import type { DocumentPath } from './types.js';
10
2
  type $$ComponentProps = {
11
- path: any;
12
- count: any;
3
+ path: DocumentPath;
4
+ count: number;
13
5
  };
6
+ declare const NodeGapMarkers: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type NodeGapMarkers = ReturnType<typeof NodeGapMarkers>;
8
+ export default NodeGapMarkers;
14
9
  //# sourceMappingURL=NodeGapMarkers.svelte.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NodeGapMarkers.svelte.d.ts","sourceRoot":"","sources":["../src/lib/NodeGapMarkers.svelte.js"],"names":[],"mappings":";;;;;AAgKA;UA3H0B,GAAG;WAAS,GAAG;WA2HoB;wBA3H3C;IAAE,IAAI,EAAE,GAAG,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE"}
1
+ {"version":3,"file":"NodeGapMarkers.svelte.d.ts","sourceRoot":"","sources":["../src/lib/NodeGapMarkers.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAuB,MAAM,YAAY,CAAC;AAEnE,KAAK,gBAAgB,GAAI;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAoKhE,QAAA,MAAM,cAAc,sDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -1,12 +1,13 @@
1
- <script>
1
+ <script lang="ts">
2
2
  import { getContext } from 'svelte';
3
3
  import { serialize_path } from './utils.js';
4
+ import type { DocumentPath, SveditContext } from './types.js';
4
5
 
5
- const svedit = getContext('svedit');
6
+ const svedit = getContext<SveditContext>('svedit');
6
7
  let selected_node_paths = $derived(get_selected_node_paths());
7
8
 
8
- function get_selected_node_paths() {
9
- const paths = [];
9
+ function get_selected_node_paths(): DocumentPath[] | undefined {
10
+ const paths: DocumentPath[] = [];
10
11
  const selection = svedit.session.selection;
11
12
  if (!selection) return;
12
13
  if (selection.type !== 'node' || selection.anchor_offset === selection.focus_offset) return;
@@ -36,10 +37,23 @@
36
37
  <style>
37
38
  .selected-node-overlay,
38
39
  .selected-property-overlay {
40
+ /* Selection frame: a single 1px outline, offset -0.5px so the
41
+ hairline centers on the node's edge and neighboring frames merge
42
+ into one shared line. Alternatives tried and rejected:
43
+ - Plain 1px border: adjacent selected nodes stack their borders
44
+ into a 2px seam between them, which looks ugly.
45
+ - Thick translucent frame (8px border) plus an inner hairline
46
+ outline: multiple nested lines take more cognitive effort to
47
+ parse than a single line.
48
+ - Inset box-shadow rings (1px stroke + 8px translucent): same
49
+ layered-frame look, same objection.
50
+ Performance note: outlines don't participate in layout, so
51
+ selection changes can't trigger reflows. */
39
52
  position: absolute;
40
53
  background: var(--svedit-editing-fill);
41
- border: 1px solid var(--svedit-editing-stroke);
42
- border-radius: 2px;
54
+ outline: 1px solid var(--svedit-editing-stroke);
55
+ outline-offset: -0.5px;
56
+ border-radius: 1px;
43
57
  top: anchor(top);
44
58
  left: anchor(left);
45
59
  bottom: anchor(bottom);
@@ -1,7 +1,4 @@
1
- export default NodeSelectionMarkers;
2
- type NodeSelectionMarkers = {
3
- $on?(type: string, callback: (e: any) => void): () => void;
4
- $set?(props: Partial<Record<string, never>>): void;
5
- };
6
1
  declare const NodeSelectionMarkers: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type NodeSelectionMarkers = ReturnType<typeof NodeSelectionMarkers>;
3
+ export default NodeSelectionMarkers;
7
4
  //# sourceMappingURL=NodeSelectionMarkers.svelte.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NodeSelectionMarkers.svelte.d.ts","sourceRoot":"","sources":["../src/lib/NodeSelectionMarkers.svelte.js"],"names":[],"mappings":";;;;;AA0CA,8FAAmE"}
1
+ {"version":3,"file":"NodeSelectionMarkers.svelte.d.ts","sourceRoot":"","sources":["../src/lib/NodeSelectionMarkers.svelte.ts"],"names":[],"mappings":"AA4CA,QAAA,MAAM,oBAAoB,2DAAwC,CAAC;AACnE,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACpE,eAAe,oBAAoB,CAAC"}
@@ -1,55 +1,58 @@
1
- /**
2
- * @typedef {Object} SessionOptions
3
- * @property {Selection} [selection] - Initial selection state
4
- */
5
- export default class Session {
6
- /**
7
- * @param {DocumentSchema} schema - The document schema
8
- * @param {Document} doc - The document
9
- * @param {object} config - configuration object
10
- * @param {Object} [options]
11
- * @param {Selection} [options.selection] - Initial selection state
12
- */
13
- constructor(schema: DocumentSchema, doc: Document, config: object, options?: {
14
- selection?: Selection;
15
- });
16
- /** @type {DocumentSchema} */
17
- schema: DocumentSchema;
18
- /** @type {Document} */
1
+ import Transaction from './Transaction.svelte.js';
2
+ import type { SelectedAttachment } from './doc_utils.js';
3
+ import type { NodeId, DocumentPath, Selection, Attachment, NodeKind, DocumentSchema, DocumentNode, Document, AnyNode, Text, CommandRegistry, Inspection, SessionConfig, SveditContext } from './types.js';
4
+ import type { Keymap } from './KeyMapper.svelte.js';
5
+ type HistoryEntry = {
6
+ ops: Transaction['ops'];
7
+ inverse_ops: Transaction['inverse_ops'];
8
+ selection_before: Selection | null;
9
+ selection_after: Selection | null;
10
+ };
11
+ export default class Session<S extends DocumentSchema = DocumentSchema> {
12
+ #private;
13
+ schema: S;
19
14
  doc: Document;
20
- /** @type {any} */
21
- config: any;
22
- history: any[];
15
+ config: SessionConfig;
16
+ history: HistoryEntry[];
23
17
  history_index: number;
24
- last_batch_started: any;
25
- commands: {};
26
- keymap: {};
18
+ last_batch_started: number | undefined;
19
+ commands: CommandRegistry;
20
+ keymap: Keymap;
27
21
  can_undo: boolean;
28
22
  can_redo: boolean;
29
- selected_node: any;
30
- available_annotation_types: any;
23
+ selected_node: AnyNode<S> | null;
24
+ available_mark_types: string[];
25
+ available_annotation_types: string[];
26
+ selected_marks: SelectedAttachment[];
27
+ active_mark: SelectedAttachment | null;
28
+ selected_annotations: SelectedAttachment[];
29
+ active_annotation: SelectedAttachment | null;
30
+ /**
31
+ * @param schema - The document schema
32
+ * @param doc - The document
33
+ * @param config - configuration object
34
+ * @param options - Optional settings (e.g. initial selection state)
35
+ */
36
+ constructor(schema: S, doc: Document, config: SessionConfig, options?: {
37
+ selection?: Selection | null;
38
+ });
39
+ /**
40
+ * Gets the current selection
41
+ */
42
+ get selection(): Selection | null;
31
43
  /**
32
44
  * Sets the selection with validation
33
- * @param {Selection | null} value - The new selection
34
45
  * @throws {Error} Throws if the selection is invalid
35
46
  */
36
47
  set selection(value: Selection | null);
37
- /**
38
- * Gets the current selection
39
- * @returns {Selection | null}
40
- */
41
- get selection(): Selection | null;
42
48
  /**
43
49
  * Validates that a selection is within bounds and refers to valid paths.
44
50
  *
45
- * @param {Selection} selection - The selection to validate
46
51
  * @throws {Error} Throws if the selection is invalid
47
- * @private
48
52
  */
49
53
  private _validate_selection;
50
54
  /**
51
55
  * Gets the document_id from the doc
52
- * @returns {string}
53
56
  */
54
57
  get document_id(): string;
55
58
  /**
@@ -60,11 +63,10 @@ export default class Session {
60
63
  * deleted nodes. That catches local node invariants and dangling references left
61
64
  * behind by deletes without scanning every node on every apply.
62
65
  *
63
- * @param {Transaction} transaction - The transaction result to validate
64
66
  * @throws {Error} Throws if the transaction result is invalid
65
67
  */
66
68
  validate_transaction_result(transaction: Transaction): void;
67
- generate_id(): any;
69
+ generate_id(): string;
68
70
  /**
69
71
  * Initialize commands and keymap for this session.
70
72
  * Called by Svedit component when it has the necessary context.
@@ -73,34 +75,34 @@ export default class Session {
73
75
  * For multiple editors on the same document, this architecture would need
74
76
  * to be refactored to support multiple sessions per document.
75
77
  *
76
- * @param {object} context - The svedit context with session, editable, canvas, etc.
78
+ * @param context - The svedit context with session, editable, canvas, etc.
77
79
  */
78
- initialize_commands(context: object): void;
79
- get_available_annotation_types(): any;
80
- get_selected_node(): any;
80
+ initialize_commands(context: SveditContext<S>): void;
81
+ get_available_mark_types(): string[];
82
+ get_available_annotation_types(): string[];
83
+ get_selected_node(): AnyNode<S> | null;
81
84
  /**
82
85
  * Creates a new transaction for making atomic changes to the document.
83
- *
84
- * @returns {Transaction} A new transaction instance
85
86
  */
86
87
  get tr(): Transaction;
87
88
  /**
88
89
  * Applies a transaction to the document.
89
- * Auto-batches history entries with debounced behavior (max one entry per 2 seconds) when batch is true.
90
+ * Auto-batches history entries with debounced behavior (max one entry per second) when batch is true.
90
91
  *
91
- * @param {Transaction} transaction - The transaction to apply
92
- * @param {object} [options] - Optional configuration
93
- * @param {boolean} [options.batch=false] - Whether to allow batching with previous transaction
92
+ * @param transaction - The transaction to apply
93
+ * @param options - Optional configuration (batch: whether to allow batching with previous transaction)
94
94
  */
95
95
  apply(transaction: Transaction, { batch }?: {
96
96
  batch?: boolean;
97
97
  }): this;
98
- undo(): this;
99
- redo(): this;
98
+ undo(): this | undefined;
99
+ redo(): this | undefined;
100
100
  /**
101
101
  * Gets a node instance or property value at the specified path.
102
- * @param {DocumentPath|string} path - Path to the node or property
103
- * @returns {any} Either a node instance object or the value of a property
102
+ *
103
+ * The result type defaults to `any` because a path can address a node or
104
+ * any property value. Annotate the target to type the result.
105
+ *
104
106
  * @example
105
107
  * // Get a node by ID
106
108
  * session.get('list_1') // => { type: 'list', id: 'list_1', ... }
@@ -114,10 +116,10 @@ export default class Session {
114
116
  * session.get(['page_1', 'body', 3, 'list_items', 0]) // => { type: 'list_item', id: 'list_item_1', ... }
115
117
  *
116
118
  * @example
117
- * // Get an annotated text property
118
- * session.get(['page_1', 'cover', 'title']) // => {text: 'Hello world', annotations: []}
119
+ * // Get a text property
120
+ * session.get(['page_1', 'cover', 'title']) // => {content: 'Hello world', marks: [], annotations: []}
119
121
  */
120
- get(path: DocumentPath | string): any;
122
+ get<T = any>(path: DocumentPath | string): T;
121
123
  /**
122
124
  * While .get gives you the value of a path, inspect gives you
123
125
  * the type info of that value.
@@ -140,50 +142,40 @@ export default class Session {
140
142
  * type: 'paragraph',
141
143
  * properties: {...}
142
144
  * }
143
- *
144
- * @param {DocumentPath} path
145
- * @returns {{kind: 'property'|'node', [key: string]: any}}
146
145
  */
147
- inspect(path: DocumentPath): {
148
- kind: "property" | "node";
149
- [key: string]: any;
150
- };
146
+ inspect(path: DocumentPath): Inspection;
151
147
  /**
152
- * Determines the kind of a node ('block' for structured blocks, 'text' for pure
153
- * text nodes or 'annotation' for annotation nodes.
154
- * @param {any} node
155
- * @returns {NodeKind}
148
+ * Determines the kind of a node ('document' for root nodes, 'block' for
149
+ * structured blocks, 'text' for pure text nodes, 'mark' for mark nodes or
150
+ * 'annotation' for annotation nodes).
156
151
  */
157
- kind(node: any): NodeKind;
152
+ kind(node: DocumentNode): NodeKind;
158
153
  /**
159
154
  * Determines whether a node type can be inserted at a given selection.
160
- * @param {string} node_type - The type of node to insert.
161
- * @param {Selection} [selection] - The selection at which to insert the node.
162
- * @returns {boolean} True if the node type can be inserted, false otherwise.
163
- */
164
- can_insert(node_type: string, selection?: Selection): boolean;
165
- /**
166
- * Compute next possible insert position from a given selection
167
155
  *
168
- * @param {Selection} [selection] - Reference selection
169
- * @returns {Selection|null} The next node insert caret selection, or null if none is available
156
+ * @param node_type - The type of node to insert.
157
+ * @param selection - The selection at which to insert the node.
158
+ * @returns True if the node type can be inserted, false otherwise.
170
159
  */
171
- get_next_node_insert_caret(selection?: Selection): Selection | null;
160
+ can_insert(node_type: string, selection?: Selection | null): boolean;
172
161
  /**
173
- * Returns the annotation object that is currently "under the cursor".
174
- * NOTE: Annotations in Svedit are exclusive, so there can only be one active_annotation
162
+ * Compute next possible insert position from a given selection
175
163
  *
176
- * @param {string} [annotation_type] Optional annotation type to filter by
177
- * @returns {Annotation|null}
178
- */
179
- active_annotation(annotation_type?: string): Annotation | null;
180
- get_selected_annotated_text(): {
181
- text: string;
182
- annotations: any;
183
- nodes: {};
184
- };
185
- get_selected_plain_text(): string;
186
- get_selected_nodes(): any;
164
+ * @param selection - Reference selection
165
+ * @returns The next node insert caret selection, or null if none is available
166
+ */
167
+ get_next_node_insert_caret(selection?: Selection | null): Selection | null;
168
+ get_selected_text(): (Text & {
169
+ nodes: Record<string, DocumentNode>;
170
+ }) | null;
171
+ get_selected_annotated_nodes(): {
172
+ nodes: Record<string, DocumentNode>;
173
+ main_nodes: NodeId[];
174
+ marks: Attachment[];
175
+ annotations: Attachment[];
176
+ } | null;
177
+ get_selected_plain_text(): string | null;
178
+ get_selected_nodes(): NodeId[] | null;
187
179
  select_parent(): void;
188
180
  /**
189
181
  * Traverses the document and returns a list of nodes in depth-first order.
@@ -193,9 +185,7 @@ export default class Session {
193
185
  * 2. Branch nodes second
194
186
  * 3. Root node (entry point) last
195
187
  *
196
- * @param {string} node_id - The ID of the node to start traversing from
197
- * @returns {Array<DocumentNode>} Array of nodes in depth-first order
198
- * @note Nodes that are not reachable from the entry point node will not be included
188
+ * Nodes that are not reachable from the entry point node will not be included.
199
189
  */
200
190
  traverse(node_id: string): Array<DocumentNode>;
201
191
  /**
@@ -203,32 +193,11 @@ export default class Session {
203
193
  *
204
194
  * We make a traversal to ensure that orphaned nodes are not included,
205
195
  * and that leaf nodes go first, followed by branches and the root node at last.
206
- *
207
- * @returns {Document} The document
208
196
  */
209
197
  to_json(): Document;
210
- property_type(type: any, property: any): string;
211
- count_references(node_id: any): number;
212
- /**
213
- * @param {NodeId} node_id
214
- * @returns {NodeId[]}
215
- */
198
+ property_type(type: string, property: string): string;
199
+ count_references(node_id: NodeId): number;
216
200
  get_referenced_nodes(node_id: NodeId): NodeId[];
217
- #private;
218
201
  }
219
- export type SessionOptions = {
220
- /**
221
- * - Initial selection state
222
- */
223
- selection?: Selection;
224
- };
225
- import type { DocumentSchema } from './types.d.ts';
226
- import type { Document } from './types.d.ts';
227
- import type { Selection } from './types.d.ts';
228
- import Transaction from './Transaction.svelte.js';
229
- import type { DocumentPath } from './types.d.ts';
230
- import type { NodeKind } from './types.d.ts';
231
- import type { Annotation } from './types.d.ts';
232
- import type { DocumentNode } from './types.d.ts';
233
- import type { NodeId } from './types.d.ts';
202
+ export {};
234
203
  //# sourceMappingURL=Session.svelte.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Session.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Session.svelte.js"],"names":[],"mappings":"AAgCA;;;GAGG;AAEH;IA+BC;;;;;;OAMG;IACH,oBANW,cAAc,OACd,QAAQ,UACR,MAAM,YAEd;QAA4B,SAAS,GAA7B,SAAS;KACnB,EAYA;IA7CD,6BAA6B;IAC7B,QADW,cAAc,CACH;IAEtB,uBAAuB;IACvB,KADW,QAAQ,CACA;IAEnB,kBAAkB;IAClB,QADW,GAAG,CACQ;IAEtB,eAAyB;IACzB,sBAA+B;IAC/B,wBAA2C;IAI3C,aAA0B;IAC1B,WAAwB;IAGxB,kBAA6C;IAC7C,kBAAkE;IAGlE,mBAAmD;IACnD,gCAA6E;IA+B7E;;;;OAIG;IACH,qBAHW,SAAS,GAAG,IAAI,EAM1B;IAhBD;;;OAGG;IACH,iBAFa,SAAS,GAAG,IAAI,CAI5B;IAYD;;;;;;OAMG;IACH,4BAEC;IAED;;;OAGG;IACH,mBAFa,MAAM,CAIlB;IAED;;;;;;;;;;OAUG;IACH,yCAHW,WAAW,QA4BrB;IAED,mBAMC;IAED;;;;;;;;;OASG;IACH,6BAFW,MAAM,QAQhB;IAED,sCAKC;IAGD,yBAkBC;IAED;;;;OAIG;IACH,UAFa,WAAW,CAKvB;IAED;;;;;;;OAOG;IACH,mBAJW,WAAW,cAEnB;QAA0B,KAAK,GAAvB,OAAO;KACjB,QA6CA;IAED,aAgBC;IAED,aAaC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,UAlBW,YAAY,GAAC,MAAM,GACjB,GAAG,CAmBf;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,cAHW,YAAY,GACV;QAAC,IAAI,EAAE,UAAU,GAAC,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAC,CAIzD;IAED;;;;;OAKG;IACH,WAHW,GAAG,GACD,QAAQ,CAIpB;IAED;;;;;OAKG;IACH,sBAJW,MAAM,cACN,SAAS,GACP,OAAO,CAcnB;IAED;;;;;OAKG;IACH,uCAHW,SAAS,GACP,SAAS,GAAC,IAAI,CAe1B;IAED;;;;;;OAMG;IACH,oCAHW,MAAM,GACJ,UAAU,GAAC,IAAI,CAI3B;IAED;;;;MA6BC;IAGD,kCAOC;IAED,0BAOC;IAED,sBAmCC;IAED;;;;;;;;;;;OAWG;IACH,kBAJW,MAAM,GACJ,KAAK,CAAC,YAAY,CAAC,CAK/B;IAED;;;;;;;OAOG;IACH,WAFa,QAAQ,CAWpB;IAID,gDAEC;IAGD,uCAEC;IAGD;;;OAGG;IACH,8BAHW,MAAM,GACJ,MAAM,EAAE,CAOpB;;CACD;;;;;gBA5hBa,SAAS;;oCAPb,cAAc;8BAAd,cAAc;+BAAd,cAAc;wBA3BA,yBAAyB;kCA2BvC,cAAc;8BAAd,cAAc;gCAAd,cAAc;kCAAd,cAAc;4BAAd,cAAc"}
1
+ {"version":3,"file":"Session.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Session.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,yBAAyB,CAAC;AAoBlD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,KAAK,EACX,MAAM,EACN,YAAY,EACZ,SAAS,EACT,UAAU,EACV,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,IAAI,EAEJ,eAAe,EACf,UAAU,EACV,aAAa,EACb,aAAa,EACb,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAIpD,KAAK,YAAY,GAAG;IACnB,GAAG,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;IACxC,gBAAgB,EAAE,SAAS,GAAG,IAAI,CAAC;IACnC,eAAe,EAAE,SAAS,GAAG,IAAI,CAAC;CAClC,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,OAAO,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc;;IAGrE,MAAM,EAAE,CAAC,CAAqB;IAE9B,GAAG,EAAE,QAAQ,CAA4B;IAEzC,MAAM,EAAE,aAAa,CAAkB;IAEvC,OAAO,EAAE,YAAY,EAAE,CAAkB;IACzC,aAAa,SAAkB;IAC/B,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAyB;IAI/D,QAAQ,EAAE,eAAe,CAAkB;IAC3C,MAAM,EAAE,MAAM,CAAkB;IAGhC,QAAQ,UAAqC;IAC7C,QAAQ,UAA0D;IAGlE,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAsC;IACtE,oBAAoB,WAA6C;IACjE,0BAA0B,WAAmD;IAC7E,cAAc,uBAAuE;IACrF,WAAW,EAAE,kBAAkB,GAAG,IAAI,CAEpC;IACF,oBAAoB,uBAA6E;IACjG,iBAAiB,EAAE,kBAAkB,GAAG,IAAI,CAE1C;IAEF;;;;;OAKG;gBAEF,MAAM,EAAE,CAAC,EACT,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,aAAa,EACrB,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAA;KAAO;IAe/C;;OAEG;IACH,IAAI,SAAS,IAAI,SAAS,GAAG,IAAI,CAEhC;IAED;;;OAGG;IACH,IAAI,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,EAGpC;IAED;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;;;;;;;;OASG;IACH,2BAA2B,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAoC3D,WAAW,IAAI,MAAM;IAUrB;;;;;;;;;OASG;IACH,mBAAmB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI;IAQpD,wBAAwB,IAAI,MAAM,EAAE;IAMpC,8BAA8B,IAAI,MAAM,EAAE;IAO1C,iBAAiB,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAoBtC;;OAEG;IACH,IAAI,EAAE,IAAI,WAAW,CAGpB;IAED;;;;;;OAMG;IACH,KAAK,CAAC,WAAW,EAAE,WAAW,EAAE,EAAE,KAAa,EAAE,GAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,IAAI;IAoDlF,IAAI,IAAI,IAAI,GAAG,SAAS;IAuBxB,IAAI,IAAI,IAAI,GAAG,SAAS;IAiBxB;;;;;;;;;;;;;;;;;;;;;OAqBG;IAEH,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,GAAG,CAAC;IAI5C;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,OAAO,CAAC,IAAI,EAAE,YAAY,GAAG,UAAU;IAIvC;;;;OAIG;IACH,IAAI,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ;IAIlC;;;;;;OAMG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,GAAE,SAAS,GAAG,IAAqB,GAAG,OAAO;IAcpF;;;;;OAKG;IACH,0BAA0B,CAAC,SAAS,GAAE,SAAS,GAAG,IAAqB,GAAG,SAAS,GAAG,IAAI;IAe1F,iBAAiB,IAAI,CAAC,IAAI,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;KAAE,CAAC,GAAG,IAAI;IAuC5E,4BAA4B,IAAI;QAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACpC,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,KAAK,EAAE,UAAU,EAAE,CAAC;QACpB,WAAW,EAAE,UAAU,EAAE,CAAC;KAC1B,GAAG,IAAI;IA2CR,uBAAuB,IAAI,MAAM,GAAG,IAAI;IASxC,kBAAkB,IAAI,MAAM,EAAE,GAAG,IAAI;IAWrC,aAAa,IAAI,IAAI;IAqCrB;;;;;;;;;OASG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC;IAQ9C;;;;;OAKG;IACH,OAAO,IAAI,QAAQ;IAanB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAKrD,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAKzC,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;CAI/C"}