@danxbot/ui 1.0.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 (183) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -0
  3. package/dist/fonts/bricolage-grotesque-0.woff2 +0 -0
  4. package/dist/fonts/bricolage-grotesque-1.woff2 +0 -0
  5. package/dist/fonts/bricolage-grotesque-2.woff2 +0 -0
  6. package/dist/fonts/figtree-0.woff2 +0 -0
  7. package/dist/fonts/figtree-1.woff2 +0 -0
  8. package/dist/fonts/figtree-2.woff2 +0 -0
  9. package/dist/fonts/figtree-3.woff2 +0 -0
  10. package/dist/fonts/jetbrains-mono-0.woff2 +0 -0
  11. package/dist/fonts/jetbrains-mono-1.woff2 +0 -0
  12. package/dist/fonts/jetbrains-mono-10.woff2 +0 -0
  13. package/dist/fonts/jetbrains-mono-11.woff2 +0 -0
  14. package/dist/fonts/jetbrains-mono-2.woff2 +0 -0
  15. package/dist/fonts/jetbrains-mono-3.woff2 +0 -0
  16. package/dist/fonts/jetbrains-mono-4.woff2 +0 -0
  17. package/dist/fonts/jetbrains-mono-5.woff2 +0 -0
  18. package/dist/fonts/jetbrains-mono-6.woff2 +0 -0
  19. package/dist/fonts/jetbrains-mono-7.woff2 +0 -0
  20. package/dist/fonts/jetbrains-mono-8.woff2 +0 -0
  21. package/dist/fonts/jetbrains-mono-9.woff2 +0 -0
  22. package/dist/fonts.css +187 -0
  23. package/dist/index.js +42425 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/styles.css +190 -0
  26. package/dist/types/components/Accordion.d.ts +15 -0
  27. package/dist/types/components/AppShell.d.ts +58 -0
  28. package/dist/types/components/Avatar.d.ts +154 -0
  29. package/dist/types/components/Badge.d.ts +73 -0
  30. package/dist/types/components/Button.d.ts +103 -0
  31. package/dist/types/components/Card.d.ts +193 -0
  32. package/dist/types/components/Choice.d.ts +112 -0
  33. package/dist/types/components/CodeEditor.d.ts +86 -0
  34. package/dist/types/components/Combobox.d.ts +37 -0
  35. package/dist/types/components/CommandPalette.d.ts +33 -0
  36. package/dist/types/components/DatePicker.d.ts +20 -0
  37. package/dist/types/components/Dialog.d.ts +117 -0
  38. package/dist/types/components/DragDrop.d.ts +60 -0
  39. package/dist/types/components/ErrorBoundary.d.ts +16 -0
  40. package/dist/types/components/Field.d.ts +37 -0
  41. package/dist/types/components/FileUpload.d.ts +22 -0
  42. package/dist/types/components/Icon.d.ts +40 -0
  43. package/dist/types/components/Input.d.ts +120 -0
  44. package/dist/types/components/Loading.d.ts +83 -0
  45. package/dist/types/components/MarkdownEditor.d.ts +24 -0
  46. package/dist/types/components/Menu.d.ts +57 -0
  47. package/dist/types/components/Navigation.d.ts +46 -0
  48. package/dist/types/components/NumberInput.d.ts +88 -0
  49. package/dist/types/components/OTPInput.d.ts +11 -0
  50. package/dist/types/components/Panel.d.ts +94 -0
  51. package/dist/types/components/Popover.d.ts +64 -0
  52. package/dist/types/components/Progress.d.ts +178 -0
  53. package/dist/types/components/Select.d.ts +53 -0
  54. package/dist/types/components/Slider.d.ts +203 -0
  55. package/dist/types/components/Spinner.d.ts +29 -0
  56. package/dist/types/components/Stat.d.ts +98 -0
  57. package/dist/types/components/StatusSelect.d.ts +23 -0
  58. package/dist/types/components/Surface.d.ts +110 -0
  59. package/dist/types/components/Table.d.ts +46 -0
  60. package/dist/types/components/Tabs.d.ts +84 -0
  61. package/dist/types/components/ThemeMenu.d.ts +4 -0
  62. package/dist/types/components/Toast.d.ts +46 -0
  63. package/dist/types/components/Toggle.d.ts +65 -0
  64. package/dist/types/components/Tree.d.ts +20 -0
  65. package/dist/types/components/calendar/CalendarMonth.d.ts +33 -0
  66. package/dist/types/components/calendar/CalendarTimeGrid.d.ts +75 -0
  67. package/dist/types/components/calendar/EventActions.d.ts +17 -0
  68. package/dist/types/components/calendar/format.d.ts +43 -0
  69. package/dist/types/components/calendar/index.d.ts +9 -0
  70. package/dist/types/components/calendar/palette.d.ts +17 -0
  71. package/dist/types/components/calendar/use-calendar-drag.d.ts +54 -0
  72. package/dist/types/components/calendar/use-event-keyboard.d.ts +24 -0
  73. package/dist/types/components/calendar/use-grid-navigation.d.ts +47 -0
  74. package/dist/types/components/calendar/use-now.d.ts +20 -0
  75. package/dist/types/components/chat/Attachment.d.ts +49 -0
  76. package/dist/types/components/chat/Chat.d.ts +60 -0
  77. package/dist/types/components/chat/Composer.d.ts +29 -0
  78. package/dist/types/components/chat/ContextMeter.d.ts +55 -0
  79. package/dist/types/components/chat/EffortControl.d.ts +23 -0
  80. package/dist/types/components/chat/Markdown.d.ts +22 -0
  81. package/dist/types/components/chat/Message.d.ts +59 -0
  82. package/dist/types/components/chat/MessageScroller.d.ts +13 -0
  83. package/dist/types/components/chat/ModelSelector.d.ts +44 -0
  84. package/dist/types/components/chat/types.d.ts +155 -0
  85. package/dist/types/components/code-editor/context.d.ts +32 -0
  86. package/dist/types/components/code-editor/editing.d.ts +39 -0
  87. package/dist/types/components/code-editor/highlight.d.ts +20 -0
  88. package/dist/types/components/code-editor/recipe.d.ts +109 -0
  89. package/dist/types/components/code-editor/state.d.ts +14 -0
  90. package/dist/types/components/drag-drop/context.d.ts +45 -0
  91. package/dist/types/components/drag-drop/recipe.d.ts +121 -0
  92. package/dist/types/components/drag-drop/shift.d.ts +66 -0
  93. package/dist/types/components/markdown/Affordances.d.ts +76 -0
  94. package/dist/types/components/rbac/AccountMenu.d.ts +8 -0
  95. package/dist/types/components/rbac/DevToolsPopover.d.ts +5 -0
  96. package/dist/types/components/rbac/ProfilePage.d.ts +1 -0
  97. package/dist/types/components/rbac/RBACProvider.d.ts +64 -0
  98. package/dist/types/components/rbac/RolePermissionMatrix.d.ts +6 -0
  99. package/dist/types/components/rbac/RolesPermissionsPage.d.ts +1 -0
  100. package/dist/types/components/rbac/index.d.ts +7 -0
  101. package/dist/types/components/rbac/types.d.ts +26 -0
  102. package/dist/types/components/roadmap/PhaseManager.d.ts +19 -0
  103. package/dist/types/components/roadmap/RoadmapItemCard.d.ts +45 -0
  104. package/dist/types/components/roadmap/RoadmapPage.d.ts +9 -0
  105. package/dist/types/components/roadmap/RoadmapSwimlaneBoard.d.ts +9 -0
  106. package/dist/types/components/roadmap/UseCaseCard.d.ts +14 -0
  107. package/dist/types/components/roadmap/UseCaseLedgerPage.d.ts +21 -0
  108. package/dist/types/components/roadmap/chip-tones.d.ts +2 -0
  109. package/dist/types/components/roadmap/derive.d.ts +12 -0
  110. package/dist/types/components/roadmap/index.d.ts +9 -0
  111. package/dist/types/components/roadmap/types.d.ts +54 -0
  112. package/dist/types/icons/data.d.ts +1318 -0
  113. package/dist/types/index.d.ts +97 -0
  114. package/dist/types/lib/calendar/band-packer.d.ts +53 -0
  115. package/dist/types/lib/calendar/drag.d.ts +125 -0
  116. package/dist/types/lib/calendar/index.d.ts +4 -0
  117. package/dist/types/lib/calendar/lane-packer.d.ts +88 -0
  118. package/dist/types/lib/calendar/segments.d.ts +91 -0
  119. package/dist/types/lib/calendar/types.d.ts +166 -0
  120. package/dist/types/lib/cn.d.ts +7 -0
  121. package/dist/types/lib/dnd/announce.d.ts +132 -0
  122. package/dist/types/lib/dnd/autoscroll.d.ts +88 -0
  123. package/dist/types/lib/dnd/geometry.d.ts +78 -0
  124. package/dist/types/lib/dnd/index.d.ts +16 -0
  125. package/dist/types/lib/dnd/sensors/gesture.d.ts +37 -0
  126. package/dist/types/lib/dnd/sensors/keyboard.d.ts +50 -0
  127. package/dist/types/lib/dnd/sensors/pointer.d.ts +59 -0
  128. package/dist/types/lib/dnd/session.d.ts +99 -0
  129. package/dist/types/lib/dnd/types.d.ts +162 -0
  130. package/dist/types/lib/dnd/use-drag.d.ts +56 -0
  131. package/dist/types/lib/icons.d.ts +40 -0
  132. package/dist/types/lib/markdown/caret-map.d.ts +48 -0
  133. package/dist/types/lib/markdown/caret.d.ts +6 -0
  134. package/dist/types/lib/markdown/commands.d.ts +170 -0
  135. package/dist/types/lib/markdown/convert.d.ts +82 -0
  136. package/dist/types/lib/markdown/dom.d.ts +67 -0
  137. package/dist/types/lib/markdown/edit.d.ts +16 -0
  138. package/dist/types/lib/markdown/history.d.ts +47 -0
  139. package/dist/types/lib/markdown/hotkeys.d.ts +39 -0
  140. package/dist/types/lib/markdown/input.d.ts +15 -0
  141. package/dist/types/lib/markdown/keys.d.ts +34 -0
  142. package/dist/types/lib/markdown/paste.d.ts +9 -0
  143. package/dist/types/lib/markdown/position.d.ts +43 -0
  144. package/dist/types/lib/markdown/project.d.ts +88 -0
  145. package/dist/types/lib/markdown/reconcile.d.ts +63 -0
  146. package/dist/types/lib/markdown/surface.d.ts +177 -0
  147. package/dist/types/lib/persist.d.ts +40 -0
  148. package/dist/types/lib/preferences.d.ts +80 -0
  149. package/dist/types/lib/sim/createSimStore.d.ts +18 -0
  150. package/dist/types/lib/sim/index.d.ts +5 -0
  151. package/dist/types/lib/sim/latency.d.ts +10 -0
  152. package/dist/types/lib/sim/registry.d.ts +14 -0
  153. package/dist/types/lib/sim/settings.d.ts +11 -0
  154. package/dist/types/lib/sim/types.d.ts +3 -0
  155. package/dist/types/lib/syntax/index.d.ts +16 -0
  156. package/dist/types/lib/syntax/languages/bash.d.ts +1 -0
  157. package/dist/types/lib/syntax/languages/css.d.ts +1 -0
  158. package/dist/types/lib/syntax/languages/html.d.ts +17 -0
  159. package/dist/types/lib/syntax/languages/index.d.ts +33 -0
  160. package/dist/types/lib/syntax/languages/javascript.d.ts +22 -0
  161. package/dist/types/lib/syntax/languages/json.d.ts +1 -0
  162. package/dist/types/lib/syntax/languages/mongodb.d.ts +1 -0
  163. package/dist/types/lib/syntax/languages/plain.d.ts +9 -0
  164. package/dist/types/lib/syntax/languages/sql.d.ts +4 -0
  165. package/dist/types/lib/syntax/languages/vue.d.ts +1 -0
  166. package/dist/types/lib/syntax/languages/yaml.d.ts +1 -0
  167. package/dist/types/lib/syntax/markdown/frames.d.ts +145 -0
  168. package/dist/types/lib/syntax/markdown/grammar.d.ts +1 -0
  169. package/dist/types/lib/syntax/markdown/index.d.ts +4 -0
  170. package/dist/types/lib/syntax/markdown/inline.d.ts +21 -0
  171. package/dist/types/lib/syntax/markdown/parse.d.ts +11 -0
  172. package/dist/types/lib/syntax/markdown/render.d.ts +19 -0
  173. package/dist/types/lib/syntax/markdown/spans.d.ts +84 -0
  174. package/dist/types/lib/syntax/markdown/types.d.ts +108 -0
  175. package/dist/types/lib/syntax/registry.d.ts +37 -0
  176. package/dist/types/lib/syntax/tokenize.d.ts +17 -0
  177. package/dist/types/lib/syntax/types.d.ts +119 -0
  178. package/dist/types/lib/time/index.d.ts +3 -0
  179. package/dist/types/lib/time/plain.d.ts +61 -0
  180. package/dist/types/lib/time/types.d.ts +87 -0
  181. package/dist/types/lib/time/zone.d.ts +97 -0
  182. package/dist/types/lib/use-media-query.d.ts +29 -0
  183. package/package.json +130 -0
@@ -0,0 +1,162 @@
1
+ /**
2
+ * The drag-and-drop contract. One session shape, produced by every input
3
+ * source, read by everything downstream.
4
+ *
5
+ * WHY NOT HTML5 DRAG-AND-DROP. The obvious design — and the one the danxbot
6
+ * dashboard's Kanban board uses today — is `draggable="true"` plus
7
+ * `dragstart`/`dragover`/`drop`. It cannot do three of the four things this
8
+ * component exists to deliver:
9
+ *
10
+ * 1. TOUCH. Mobile browsers do not fire drag events for touch input at all.
11
+ * Not throttled, not partial — absent. A board built on that API is inert
12
+ * on every phone, and nothing inside the API can fix it.
13
+ * 2. KEYBOARD. There is no keyboard path into a drag session; the events only
14
+ * exist as a consequence of a pointer gesture.
15
+ * 3. EDGE AUTO-SCROLL. `dragover` is throttled and the browser owns the
16
+ * gesture, so a scroll ramp driven from it stutters.
17
+ *
18
+ * Pointer Events give one code path for mouse, touch and pen, and leave the
19
+ * gesture under our control — so keyboard becomes a second SENSOR feeding the
20
+ * same session rather than a parallel implementation that has to be kept in
21
+ * agreement with the first one.
22
+ *
23
+ * The invariant that removes the entire off-by-one bug class, asserted by the
24
+ * geometry tests: `DropTarget.index` is the insertion index in the container's
25
+ * item list WITH THE DRAGGED ITEM ALREADY REMOVED. Same-container and
26
+ * cross-container drops are then arithmetically identical, and a consumer never
27
+ * has to ask "does this index account for the hole the item left behind".
28
+ */
29
+ /** Client-space point. Always viewport coordinates, never page. */
30
+ export interface Point {
31
+ x: number;
32
+ y: number;
33
+ }
34
+ /** A draggable item's identity. Stable across renders — never an array index. */
35
+ export type DragId = string;
36
+ /** A container that holds draggable items: a column, a list, a board lane. */
37
+ export type ContainerId = string;
38
+ /**
39
+ * The reorder axis of a container. `"y"` is a vertical list, `"x"` a row of
40
+ * columns. The axis decides which coordinate the midpoint rule reads and which
41
+ * arrow keys move within the container rather than between containers.
42
+ */
43
+ export type DragAxis = "x" | "y";
44
+ /**
45
+ * Where the dragged item lands if released now.
46
+ *
47
+ * `index` counts the container's items WITHOUT the dragged item — see the file
48
+ * header. `0` is before every remaining item, `length` is after all of them.
49
+ */
50
+ export interface DropTarget {
51
+ container: ContainerId;
52
+ index: number;
53
+ }
54
+ /**
55
+ * Which sensor started the session. Downstream behaviour genuinely differs:
56
+ * the floating clone follows a pointer but snaps to the target in keyboard
57
+ * mode, and announcements are only worth emitting for a session a screen-reader
58
+ * user could be driving.
59
+ */
60
+ export type DragMode = "pointer" | "keyboard";
61
+ /**
62
+ * `pending` is a pointer that is down on a handle but has not yet passed its
63
+ * activation constraint. It exists so a tap stays a tap and a swipe stays a
64
+ * scroll — the single most common way a drag list becomes unusable on a phone
65
+ * is starting the drag on `pointerdown`.
66
+ */
67
+ export type DragPhase = "idle" | "pending" | "dragging";
68
+ export interface DragState {
69
+ phase: DragPhase;
70
+ /** The item being carried. Non-null in `pending` and `dragging`. */
71
+ id: DragId | null;
72
+ /** Where it came from, captured at pickup so cancel can restore it. */
73
+ from: DropTarget | null;
74
+ /** Where it would land now. Never null while `dragging`. */
75
+ over: DropTarget | null;
76
+ mode: DragMode | null;
77
+ /** Live pointer position. Null in keyboard mode — there is no pointer. */
78
+ point: Point | null;
79
+ /**
80
+ * The cursor's offset inside the item at pickup, so the floating clone tracks
81
+ * the exact point that was grabbed rather than snapping a corner to the
82
+ * cursor. Null in keyboard mode.
83
+ */
84
+ grab: Point | null;
85
+ }
86
+ /** A registered draggable item. Registration is what makes it measurable. */
87
+ export interface ItemRegistration {
88
+ id: DragId;
89
+ container: ContainerId;
90
+ /** Position within its container, as the consumer's data currently orders it. */
91
+ index: number;
92
+ element: HTMLElement;
93
+ }
94
+ export interface ContainerRegistration {
95
+ id: ContainerId;
96
+ element: HTMLElement;
97
+ axis: DragAxis;
98
+ }
99
+ /** One item's measured box, taken at snapshot time. */
100
+ export interface ItemRect {
101
+ id: DragId;
102
+ container: ContainerId;
103
+ index: number;
104
+ rect: DOMRect;
105
+ }
106
+ export interface ContainerRect {
107
+ id: ContainerId;
108
+ axis: DragAxis;
109
+ rect: DOMRect;
110
+ }
111
+ /**
112
+ * Every box measured once, at pickup.
113
+ *
114
+ * WHY A SNAPSHOT. `getBoundingClientRect` forces layout, and a drag reads every
115
+ * candidate box on every move. Measuring per `pointermove` costs a forced
116
+ * reflow per frame of the one gesture that must never drop a frame — and it
117
+ * measures boxes that are MID-ANIMATION, so the midpoint rule reads a moving
118
+ * target and the insertion point oscillates. The snapshot is invalidated by
119
+ * scroll and resize, which are the only two things that move a box the drag
120
+ * itself did not.
121
+ */
122
+ export interface LayoutSnapshot {
123
+ items: ItemRect[];
124
+ containers: ContainerRect[];
125
+ }
126
+ /** Emitted once per completed drag. `from` and `to` may be equal — see `moved`. */
127
+ export interface DragEndEvent {
128
+ id: DragId;
129
+ from: DropTarget;
130
+ to: DropTarget;
131
+ mode: DragMode;
132
+ /**
133
+ * `false` when the release lands the item exactly where it started. Consumers
134
+ * MUST skip their write in that case: the dashboard's implementation churned
135
+ * its ordering field on every no-op release, and a write that changes nothing
136
+ * still costs a request, a re-render and an audit-log row.
137
+ */
138
+ moved: boolean;
139
+ }
140
+ /** Emitted at pickup, before any movement. */
141
+ export interface DragStartEvent {
142
+ id: DragId;
143
+ from: DropTarget;
144
+ mode: DragMode;
145
+ }
146
+ /** Emitted when a session ends without committing — Esc, or a cancelled pointer. */
147
+ export interface DragCancelEvent {
148
+ id: DragId;
149
+ from: DropTarget;
150
+ mode: DragMode;
151
+ }
152
+ export interface DragSessionCallbacks {
153
+ onDragStart?: (e: DragStartEvent) => void;
154
+ /** Fires on every change of `over`, never on mere pointer movement. */
155
+ onDragOver?: (e: {
156
+ id: DragId;
157
+ from: DropTarget;
158
+ over: DropTarget;
159
+ }) => void;
160
+ onDragEnd?: (e: DragEndEvent) => void;
161
+ onDragCancel?: (e: DragCancelEvent) => void;
162
+ }
@@ -0,0 +1,56 @@
1
+ import { DragSession } from "./session";
2
+ import type { PointerActivation } from "./sensors/pointer";
3
+ import type { ContainerId, DragAxis, DragId, DragSessionCallbacks, DragState } from "./types";
4
+ export declare const DragSessionProvider: import("react").Provider<DragSession | null>;
5
+ export declare function useDragSession(component: string): DragSession;
6
+ /**
7
+ * Create the session a `<DragDrop>` owns.
8
+ *
9
+ * Built ONCE, with its callbacks swapped on every render. Rebuilding it when a
10
+ * consumer's `onDragEnd` identity changes — which it does on every render
11
+ * unless they memoize, and they will not — would destroy the machine mid-gesture
12
+ * and drop the drag.
13
+ */
14
+ export declare function useCreateDragSession(callbacks: DragSessionCallbacks): DragSession;
15
+ /** Subscribe to the session. */
16
+ export declare function useDragState(session: DragSession): DragState;
17
+ export interface UseDraggableOptions {
18
+ id: DragId;
19
+ container: ContainerId;
20
+ /** Position in the consumer's data. Re-registers when it changes. */
21
+ index: number;
22
+ /**
23
+ * The reorder axis of this item's container. Decides which arrow pair steps
24
+ * within the container and which moves between containers.
25
+ *
26
+ * @default "y"
27
+ */
28
+ axis?: DragAxis;
29
+ disabled?: boolean;
30
+ activation?: PointerActivation;
31
+ }
32
+ export interface UseDraggableResult {
33
+ /** The item's own element — this is what gets measured. */
34
+ setItemRef: (el: HTMLElement | null) => void;
35
+ /**
36
+ * The drag handle. Pass the same element as `setItemRef` to make the whole
37
+ * item draggable, or a grip inside it to make only the grip draggable. They
38
+ * are deliberately separate: a card full of buttons must not start a drag
39
+ * from every one of them.
40
+ */
41
+ setHandleRef: (el: HTMLElement | null) => void;
42
+ isDragging: boolean;
43
+ }
44
+ export declare function useDraggable(session: DragSession, options: UseDraggableOptions): UseDraggableResult;
45
+ export interface UseDropContainerOptions {
46
+ id: ContainerId;
47
+ axis?: DragAxis;
48
+ }
49
+ export interface UseDropContainerResult {
50
+ setContainerRef: (el: HTMLElement | null) => void;
51
+ /** True while the carried item would land in this container. */
52
+ isOver: boolean;
53
+ /** Insertion index within this container, or null when it is not the target. */
54
+ overIndex: number | null;
55
+ }
56
+ export declare function useDropContainer(session: DragSession, options: UseDropContainerOptions): UseDropContainerResult;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Icon registry.
3
+ *
4
+ * Icons are DATA here, not components. A `[tag, attrs]` tuple list is ~100
5
+ * bytes, carries no React overhead, and can be registered at runtime — which
6
+ * is what makes the icon set swappable rather than baked in.
7
+ *
8
+ * WHY A REGISTRY AT ALL, rather than importing icon components directly:
9
+ *
10
+ * 1. Components reference icons BY NAME (`<Icon name="check" />`), so a
11
+ * consumer can re-point `check` at their own drawing and every checkbox,
12
+ * menu tick and status chip in the system follows. Direct imports would
13
+ * hard-wire our drawing into their app.
14
+ * 2. It keeps any icon library an OPTIONAL dependency. The core set below is
15
+ * shipped as data with no package behind it; anything further is
16
+ * registered by the consumer from whatever set they already use.
17
+ * 3. Names resolve at render, so an icon can be swapped per theme or per
18
+ * brand without re-rendering the tree that uses it.
19
+ *
20
+ * The core set's geometry is Lucide's (ISC licensed — see src/icons/data.ts),
21
+ * extracted to data at build time so nothing ships at runtime.
22
+ */
23
+ /** A single SVG child: an element name and its attributes. */
24
+ export type IconNodeChild = readonly [tag: string, attrs: Record<string, string | number>];
25
+ /** An icon's full geometry — the children of a 24x24 `<svg>`. */
26
+ export type IconNode = readonly IconNodeChild[];
27
+ /**
28
+ * Register (or replace) one icon. Replacement is deliberate and unguarded:
29
+ * overriding a core name with a brand's own drawing is the primary reason this
30
+ * registry exists, so it must not require a different call than adding one.
31
+ */
32
+ export declare function registerIcon(name: string, node: IconNode): void;
33
+ /** Register many at once — the shape a generated icon module exports. */
34
+ export declare function registerIcons(icons: Record<string, IconNode>): void;
35
+ /** Look up an icon, or `undefined` when the name was never registered. */
36
+ export declare function getIcon(name: string): IconNode | undefined;
37
+ /** Every registered name, sorted — powers icon galleries and docs pages. */
38
+ export declare function iconNames(): string[];
39
+ /** True when `name` resolves — for a consumer validating config-driven icons. */
40
+ export declare function hasIcon(name: string): boolean;
@@ -0,0 +1,48 @@
1
+ import type { Projection } from "./project";
2
+ /** A half-open source range. */
3
+ export interface Span {
4
+ start: number;
5
+ end: number;
6
+ }
7
+ export interface CaretMap {
8
+ source: string;
9
+ /** Every offset the caret may occupy, ascending and unique. */
10
+ stops: readonly number[];
11
+ /** The source range of every VISIBLE grapheme, in document order. */
12
+ graphemes: readonly Span[];
13
+ /**
14
+ * The stop that stands for each source offset, indexed by offset.
15
+ *
16
+ * NEEDED BECAUSE "NEAREST" IS THE WRONG QUESTION. Deleting the "d" from
17
+ * `**bold**` leaves the caret at offset 5, which is the same PLACE as offset
18
+ * 7 but two characters closer to 4 — so a nearest-by-distance snap puts the
19
+ * caret before the "l" instead of after the construct, and the next keystroke
20
+ * lands in the wrong word. An offset belongs to the coincident group it falls
21
+ * in, and only this table knows which that is.
22
+ */
23
+ representative: readonly number[];
24
+ }
25
+ /**
26
+ * Builds the map for a projection.
27
+ *
28
+ * Pure, and rebuilt with every projection rather than maintained across edits —
29
+ * the same reasoning the projection itself gives. Nothing is kept in step, so
30
+ * nothing can drift.
31
+ */
32
+ export declare function caretMap(projection: Projection): CaretMap;
33
+ /**
34
+ * The legal offset standing for `offset` — the caret's real place.
35
+ *
36
+ * Not "the closest number in `stops`": see `representative`. Out-of-range
37
+ * offsets clamp, because a caret that cannot be placed is worse than one placed
38
+ * at the edge.
39
+ */
40
+ export declare function nearestStop(map: CaretMap, offset: number): number;
41
+ /** The next legal offset in `direction`, or the same one at a document edge. */
42
+ export declare function stepStop(map: CaretMap, offset: number, direction: 1 | -1): number;
43
+ /** The visible grapheme immediately before `offset`, if there is one. */
44
+ export declare function graphemeBefore(map: CaretMap, offset: number): Span | null;
45
+ /** The visible grapheme immediately after `offset`, if there is one. */
46
+ export declare function graphemeAfter(map: CaretMap, offset: number): Span | null;
47
+ /** The first legal offset on the line holding `offset` — where Home goes. */
48
+ export declare function lineStartStop(map: CaretMap, offset: number): number;
@@ -0,0 +1,6 @@
1
+ export declare function stepOffset(source: string, offset: number, direction: 1 | -1): number;
2
+ /** The source range of the line containing `offset`, terminator excluded. */
3
+ export declare function lineAt(source: string, offset: number): {
4
+ start: number;
5
+ end: number;
6
+ };
@@ -0,0 +1,170 @@
1
+ import type { SourceSelection } from "./position";
2
+ import type { Splice } from "./surface";
3
+ export interface Edit {
4
+ splice: Splice;
5
+ select: SourceSelection;
6
+ }
7
+ /** A command returns null when it does not apply, so the key falls through. */
8
+ export type Command = (source: string, selection: SourceSelection) => Edit | null;
9
+ interface LineSpan {
10
+ start: number;
11
+ end: number;
12
+ text: string;
13
+ }
14
+ /**
15
+ * Toggles a symmetric inline marker around the selection.
16
+ *
17
+ * WRAPPING AND UNWRAPPING ARE THE SAME COMMAND, and which one happens is
18
+ * decided by looking at the source on both sides — not by asking the rendered
19
+ * tree whether the selection "is bold". The tree answer is ambiguous the moment
20
+ * a selection spans a boundary; the source answer never is.
21
+ */
22
+ export declare function toggleInline(marker: string): Command;
23
+ export declare const toggleBold: Command;
24
+ export declare const toggleItalic: Command;
25
+ export declare const toggleCode: Command;
26
+ export declare const toggleStrike: Command;
27
+ /** The heading level of a line, 0 for a paragraph. */
28
+ export declare function headingLevel(text: string): number;
29
+ /**
30
+ * Sets a line's heading level. Level 0 removes the heading.
31
+ *
32
+ * Applied to the FIRST line of the selection only. A multi-line selection means
33
+ * "make this a heading", and a heading is one line — turning six selected lines
34
+ * into six headings is a destructive reading of an ambiguous gesture.
35
+ */
36
+ export declare function setHeading(level: number): Command;
37
+ export declare function adjustHeading(delta: number): Command;
38
+ /**
39
+ * Toggles a list over every line the selection touches.
40
+ *
41
+ * Removes only when EVERY line already has that kind of marker — the same
42
+ * convention every editor uses, and the one that makes a mixed selection do
43
+ * something predictable rather than alternating line by line.
44
+ */
45
+ export declare function toggleList(asOrdered: boolean): Command;
46
+ export declare const indentLines: Command;
47
+ export declare const outdentLines: Command;
48
+ /** Toggles a task checkbox, adding one if the item has none. */
49
+ export declare const toggleTask: Command;
50
+ export declare const toggleQuote: Command;
51
+ /**
52
+ * Wraps the selected lines in a fence, or unwraps the fence they are inside.
53
+ *
54
+ * The fence is built from the lines OUTSIDE the selection when unwrapping, so
55
+ * putting the caret anywhere in a code block and pressing the key removes the
56
+ * block — rather than requiring the user to select the fence lines they cannot
57
+ * see.
58
+ */
59
+ export declare function toggleCodeBlock(language?: string): Command;
60
+ /** The fenced block containing `offset`, if any. */
61
+ export declare function fenceAround(source: string, offset: number): {
62
+ start: number;
63
+ end: number;
64
+ bodyStart: number;
65
+ bodyEnd: number;
66
+ } | null;
67
+ export declare const insertRule: Command;
68
+ /**
69
+ * Wraps the selection as a link, leaving the caret in the destination.
70
+ *
71
+ * With no href the destination is empty and the caret sits inside the
72
+ * parentheses, which is where a popover would put it too — the command is the
73
+ * same whether a popover drove it or a hotkey did.
74
+ */
75
+ export declare function insertLink(href?: string, title?: string): Command;
76
+ /**
77
+ * Inserts an image, using the selection as its alt text.
78
+ *
79
+ * ALT TEXT IS NOT OPTIONAL AND NOT AN AFTERTHOUGHT, which is why the selection
80
+ * becomes it rather than the source: an image whose alt is the filename is
81
+ * exactly as useless to a screen reader as one with none, and the person
82
+ * inserting it is the only one who knows what it shows. With nothing selected
83
+ * the caret lands between the brackets, on the alt, before the URL.
84
+ */
85
+ export declare function insertImage(src?: string, alt?: string): Command;
86
+ export interface TableSpan {
87
+ start: number;
88
+ end: number;
89
+ /** Whole lines: header, delimiter, then body rows. */
90
+ rows: LineSpan[];
91
+ }
92
+ /** The GFM table containing `offset`, if any. */
93
+ export declare function tableAround(source: string, offset: number): TableSpan | null;
94
+ /** A row's cells, by their text, with the outer pipes removed. */
95
+ export declare function cellsOf(text: string): string[];
96
+ /** Which row and column an offset falls in, within a table. */
97
+ export declare function cellAt(source: string, table: TableSpan, offset: number): {
98
+ row: number;
99
+ column: number;
100
+ } | null;
101
+ export declare function insertTable(columns?: number, bodyRows?: number): Command;
102
+ export declare function insertRow(below: boolean): Command;
103
+ export declare function insertColumn(after: boolean): Command;
104
+ export declare const deleteRow: Command;
105
+ export declare const deleteColumn: Command;
106
+ export declare const deleteTable: Command;
107
+ export declare function alignColumn(align: "left" | "center" | "right"): Command;
108
+ /**
109
+ * Moves the caret to the next or previous CELL of the table it is in.
110
+ *
111
+ * Tab between cells is the one table affordance people notice the absence of
112
+ * immediately, because every spreadsheet and every other table editor has it.
113
+ * It is a MOVEMENT, not an edit, so it returns a zero-width splice — the same
114
+ * shape every other command uses, which keeps one code path in the caller.
115
+ *
116
+ * Wraps to the next ROW at the end of one, and stops at the table's ends rather
117
+ * than escaping into the document: a Tab that leaves the table by surprise is
118
+ * how a user loses their place in it.
119
+ */
120
+ export declare function tableTab(forward: boolean): Command;
121
+ /**
122
+ * Enter, with the block structure carried onto the next line.
123
+ *
124
+ * The one place a command looks at what the line IS rather than what the user
125
+ * selected — because pressing Enter in a list and getting a bare line is the
126
+ * single most-noticed omission in a markdown editor.
127
+ *
128
+ * An EMPTY list item exits the list instead of making another one, which is
129
+ * what every editor does and what stops a stray marker being left behind.
130
+ */
131
+ /**
132
+ * Backspace at the start of a block's own text, which removes that block's
133
+ * markup instead of the character before it.
134
+ *
135
+ * THIS IS THE ONLY WAY TO REMOVE MARKUP YOU CANNOT SEE. Under the old contract
136
+ * the marker was reachable text, so deleting a list item's dash was just
137
+ * backspacing over a dash. Now the caret cannot enter a marker at all, and the
138
+ * character before an item's first letter is the newline that ends the line
139
+ * above — so a plain backspace would MERGE THE ITEM INTO THE PREVIOUS LINE and
140
+ * leave `previous- item` behind, printing a marker as prose. Every rich editor
141
+ * uses this rule for the same reason; here it is load-bearing rather than
142
+ * conventional.
143
+ *
144
+ * It is a LADDER, one rung per press, so holding backspace unwinds a nested
145
+ * item cleanly instead of jumping to plain text: checkbox, then one indent
146
+ * level per press, then the marker itself.
147
+ *
148
+ * Returns null unless the caret is collapsed AT OR BEFORE the block's content
149
+ * start — anywhere past it, backspace means what it always meant.
150
+ *
151
+ * AT OR BEFORE, not exactly at, because with the markup collapsed every offset
152
+ * from the line's start to its content start is the SAME PLACE on screen, and
153
+ * which of them the caret map picks depends on what else is on the line. An
154
+ * empty task item holding a table turned out to put it at offset 0 rather than
155
+ * at 6, and an exact test then declined — leaving a document that rendered as a
156
+ * checkbox and an empty table with no key that could remove either.
157
+ */
158
+ export declare const backspaceBlockPrefix: Command;
159
+ export declare const smartEnter: Command;
160
+ /**
161
+ * Tab: indent inside a list, otherwise decline so focus can leave.
162
+ *
163
+ * WCAG 2.1.2 permits an editor to own Tab, but the advisement clause is
164
+ * normative — an undocumented escape is a failure even when the escape exists.
165
+ * Declining outside a list means Tab does the ordinary thing almost everywhere,
166
+ * which is the cheapest possible way to satisfy it.
167
+ */
168
+ export declare const smartTab: Command;
169
+ export declare const smartShiftTab: Command;
170
+ export {};
@@ -0,0 +1,82 @@
1
+ import { type Command, type Edit } from "./commands";
2
+ import type { Projection } from "./project";
3
+ /** The block kinds a line-type control can offer. */
4
+ export type BlockKind = "paragraph" | "heading" | "quote" | "code" | "bullet" | "ordered" | "task" | "table" | "rule";
5
+ export interface BlockInfo {
6
+ kind: BlockKind;
7
+ /** 1–6 for a heading, otherwise undefined. */
8
+ level?: number;
9
+ /** The info string of a fence, "" when it has none. */
10
+ language?: string;
11
+ /** Whether a task item is ticked. */
12
+ checked?: boolean;
13
+ /** The source span this control operates on. */
14
+ start: number;
15
+ end: number;
16
+ }
17
+ /**
18
+ * What the block at `offset` is, and the span a conversion would replace.
19
+ *
20
+ * The span is the whole construct for a fence or a table and the caret's own
21
+ * line otherwise — see the header. A caller with a real selection widens it
22
+ * itself; this answers for a point.
23
+ */
24
+ export declare function blockAt(source: string, offset: number): BlockInfo;
25
+ export interface ConvertOptions {
26
+ /** 1–6, for `heading`. Ignored otherwise. */
27
+ level?: number;
28
+ /** Info string, for `code`. Ignored otherwise. */
29
+ language?: string;
30
+ }
31
+ /**
32
+ * Converts the block at the selection to `kind`, whatever it is now.
33
+ *
34
+ * SET, NOT TOGGLE: converting a quote to a quote is a no-op rather than an
35
+ * un-quote, because a menu item that undoes itself when the current type is
36
+ * picked is a menu item that punishes confirming what you already have.
37
+ */
38
+ export declare function convertBlock(kind: BlockKind, options?: ConvertOptions): Command;
39
+ /**
40
+ * Rewrites a fenced block's info string.
41
+ *
42
+ * Its own command rather than `convertBlock("code", {language})`, because that
43
+ * would rebuild the whole block to change one word — losing nothing, but
44
+ * replacing every line and therefore every DOM node the reconciler had kept.
45
+ */
46
+ export declare function setFenceLanguage(language: string): Command;
47
+ export interface ConstructInfo {
48
+ kind: "link" | "image";
49
+ /** Destination — `href` for a link, `src` for an image. */
50
+ target: string;
51
+ /** Label for a link, alt text for an image. */
52
+ label: string;
53
+ title?: string;
54
+ start: number;
55
+ end: number;
56
+ }
57
+ /**
58
+ * The innermost link or image containing `offset`.
59
+ *
60
+ * Read from the PROJECTION rather than by matching brackets, for the reason
61
+ * every other reader in this codebase gives: the parser has already decided
62
+ * what is a link, including the cases a regex gets wrong — a bracket inside a
63
+ * code span, an unbalanced one, a reference definition elsewhere in the file.
64
+ */
65
+ export declare function constructAt(projection: Projection, offset: number): ConstructInfo | null;
66
+ export interface ConstructPatch {
67
+ target?: string;
68
+ label?: string;
69
+ title?: string;
70
+ /** Rewrites a link as an image or the other way round. */
71
+ kind?: "link" | "image";
72
+ }
73
+ /** Rewrites the link or image at `offset` from its parts. */
74
+ export declare function editConstruct(projection: Projection, offset: number, patch: ConstructPatch): Edit | null;
75
+ /**
76
+ * Replaces a link or image with its own text.
77
+ *
78
+ * The one conversion that removes a construct rather than changing it, and the
79
+ * only way to get rid of a link now that its brackets are markup the caret
80
+ * cannot reach.
81
+ */
82
+ export declare function unwrapConstruct(projection: Projection, offset: number): Edit | null;
@@ -0,0 +1,67 @@
1
+ import type { Frame } from "../syntax/markdown/frames";
2
+ /** Marks the wrapper around a run of markup, for the collapse rule. */
3
+ export declare const SYNTAX_ATTR = "data-md-syntax";
4
+ /** Names the frame an element came from, for styling and for the reconciler. */
5
+ export declare const FRAME_ATTR = "data-md";
6
+ export interface TagSpec {
7
+ tag: string;
8
+ /** Attributes the element carries, beyond `data-md`. */
9
+ attrs?: Record<string, string>;
10
+ /**
11
+ * A rendered decoration appended after the frame's own characters.
12
+ *
13
+ * THE ONLY WAY TO SHOW A PICTURE WITHOUT LYING ABOUT THE TEXT. An image's
14
+ * source is `![alt](src)` and every one of those characters must stay in the
15
+ * DOM and stay reachable — so the picture cannot replace them. It is an extra
16
+ * node that holds NO TEXT, which is exactly why it is invisible to the
17
+ * projection invariant and to position mapping: both walk text nodes only.
18
+ *
19
+ * Marked with its own attribute so the reconciler can lift it out before
20
+ * diffing children and put it back after, the same way the trailing-line
21
+ * filler is handled. Without that it would occupy a child index and shift
22
+ * every sibling the positional diff is counting.
23
+ */
24
+ decoration?: {
25
+ tag: string;
26
+ attrs: Record<string, string>;
27
+ };
28
+ }
29
+ /** Marks a rendered decoration, which carries no text and no source range. */
30
+ export declare const DECORATION_ATTR = "data-md-decoration";
31
+ /**
32
+ * What an image's `src` may be — a DIFFERENT policy from a link's `href`, and
33
+ * deliberately so.
34
+ *
35
+ * `sanitizeHref` allows only `http(s)`, `mailto`, `tel` and `ftp`, because a
36
+ * link is something a person clicks and `data:text/html` is a working attack.
37
+ * That rule is right for links and it is NOT loosened here.
38
+ *
39
+ * An image is not clicked, and `data:image/*` is how an inline avatar, an icon
40
+ * or a pasted screenshot is written — refusing it would make the editor unable
41
+ * to show a very ordinary document. The narrow addition is safe for one
42
+ * specific reason worth stating: an SVG loaded through `<img>` has scripting
43
+ * DISABLED by specification, which is not true of the same SVG in an `<object>`
44
+ * or an `<iframe>`. So `data:image/*` is allowed only because the decoration is
45
+ * an `<img>` and nothing else.
46
+ */
47
+ export declare function sanitizeImageSrc(src: string): string | null;
48
+ /**
49
+ * The element a frame becomes.
50
+ *
51
+ * `a` deliberately renders as a `<span>`, NOT an anchor. An anchor inside a
52
+ * contenteditable is a trap: the browser gives it its own selection behaviour,
53
+ * some engines make it draggable, and a click navigates away from the document
54
+ * being edited. The link's appearance is a class; its behaviour belongs to the
55
+ * editor, which knows whether a modifier key was held.
56
+ */
57
+ export declare function tagFor(frame: Frame): TagSpec;
58
+ /**
59
+ * Whether two frames describe the same element, for reuse during a patch.
60
+ *
61
+ * Compared by TAG and by the attributes that would have to change, not by deep
62
+ * equality of the frame: a heading whose text changed is the same `<h2>` and
63
+ * must keep its identity, while a heading that became an `<h3>` is a different
64
+ * element. Getting this wrong in the permissive direction replaces nodes the
65
+ * caret is inside; getting it wrong in the strict direction is merely slower.
66
+ */
67
+ export declare function sameElement(a: Frame, b: Frame): boolean;
@@ -0,0 +1,16 @@
1
+ import type { CaretMap } from "./caret-map";
2
+ import type { Edit } from "./commands";
3
+ import type { Projection } from "./project";
4
+ export type { Edit } from "./commands";
5
+ /**
6
+ * Removes every visible character in `[from, to)`, and any inline construct
7
+ * that is left with nothing inside it.
8
+ *
9
+ * Returns null when the range holds no visible characters at all, so a caller
10
+ * can tell "nothing to delete" from "deleted nothing".
11
+ */
12
+ export declare function deleteVisible(projection: Projection, from: number, to: number): Edit | null;
13
+ /** Backspace: removes the visible character before `offset`, or an atomic construct. */
14
+ export declare function deleteVisibleBefore(projection: Projection, map: CaretMap, offset: number): Edit | null;
15
+ /** Forward delete: removes the visible character after `offset`, or an atomic construct. */
16
+ export declare function deleteVisibleAfter(projection: Projection, map: CaretMap, offset: number): Edit | null;