@draftably/editor 0.4.0 → 0.5.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.
@@ -274,6 +274,9 @@ export declare const useSessionIfAny: () => ({
274
274
  showState: (id: string | null) => void;
275
275
  stepState: (by: 1 | -1) => boolean;
276
276
  drawnDoc: () => import("../model/types").Diagram;
277
+ dark: import("solid-js").Accessor<boolean>;
278
+ setDark: import("solid-js").Setter<boolean>;
279
+ drawnAppearance: () => import("../model/appearance").DiagramAppearance;
277
280
  } & {
278
281
  tool: import("solid-js").Accessor<import("../state/session/editor").Tool>;
279
282
  setToolSignal: import("solid-js").Setter<import("../state/session/editor").Tool>;
@@ -7,7 +7,7 @@
7
7
  * write snaps back when the source is reprojected, so nothing unsaved looks saved. Changes the
8
8
  * file cannot express are rejected outright.
9
9
  */
10
- import type { DiagramAppearance } from '../model/appearance';
10
+ import { type DiagramAppearance } from '../model/appearance';
11
11
  import type { Diagram } from '../model/types';
12
12
  import { type Scene } from './compile';
13
13
  import { type SourceDoc } from './syntax';
@@ -268,6 +268,9 @@ export declare const sessions: WeakMap<object, {
268
268
  showState: (id: string | null) => void;
269
269
  stepState: (by: 1 | -1) => boolean;
270
270
  drawnDoc: () => import("../model/types").Diagram;
271
+ dark: import("solid-js").Accessor<boolean>;
272
+ setDark: import("solid-js").Setter<boolean>;
273
+ drawnAppearance: () => import("../model/appearance").DiagramAppearance;
271
274
  } & {
272
275
  tool: import("solid-js").Accessor<import("../state/session/editor").Tool>;
273
276
  setToolSignal: import("solid-js").Setter<import("../state/session/editor").Tool>;
@@ -3,6 +3,7 @@ import { type Camera, type CameraReason, type FrameOptions, type MoveResult } fr
3
3
  import { type Geometry } from '../runtime/geometry';
4
4
  import { type Anchor, type DraftSlot } from '../runtime/anchors';
5
5
  import type { Diagnostic } from '../runtime/types';
6
+ import type { EditorUpdate } from '../runtime/host';
6
7
  import '../styles/app.css';
7
8
  /** The options a host sets on `<draft-editor>`: every mount option but the text and `onChange`. */
8
9
  export type EditorOptions = Pick<SessionOptions, 'storageKey' | 'openLink' | 'confirm' | 'onInteract'> & {
@@ -21,12 +22,21 @@ export interface EditorEvents {
21
22
  anchors: {
22
23
  anchors: readonly Anchor[];
23
24
  };
24
- /** Slot warnings: an unknown slot, or content larger than its node. */
25
+ /** A source that does not parse (`DRAFT_PARSE`), then slot warnings: an unknown slot, or content larger than its node. */
25
26
  diagnostics: {
26
27
  revision: number;
27
28
  diagnostics: readonly Diagnostic[];
28
29
  stale: boolean;
29
30
  };
31
+ /** Each update's result, from the first text on. */
32
+ update: EditorUpdateResult;
33
+ /** A link mark was pressed. `preventDefault()` stops the editor opening it. */
34
+ link: {
35
+ itemId: string;
36
+ href: string;
37
+ target: '_blank';
38
+ preventDefault(): void;
39
+ };
30
40
  camera: {
31
41
  camera: Camera;
32
42
  reason: CameraReason;
@@ -45,11 +55,8 @@ export interface EditorEvents {
45
55
  preventDefault(): void;
46
56
  };
47
57
  }
48
- /** An editor update's outcome. The editor draws in its own session, so it has no SVG value. */
49
- export type EditorUpdateResult = {
50
- status: 'committed' | 'error' | 'destroyed';
51
- revision: number;
52
- };
58
+ /** An editor update's outcome: `EditorUpdate` in `@draftably/runtime/host`. */
59
+ export type EditorUpdateResult = EditorUpdate;
53
60
  /** The editor's handle: the viewer's camera, plus editing. */
54
61
  export interface MountedEditor {
55
62
  /** Resolves once the first text is drawn. */
@@ -9,7 +9,7 @@ import type { Footnote } from './footnote';
9
9
  import type { Note } from './note';
10
10
  import { type FontRole, type TypeProfile } from './fonts';
11
11
  import type { FileConfig } from './link';
12
- export type ThemeId = 'drafting' | 'fieldnotes' | 'signal' | 'ink' | 'blueprint';
12
+ export type ThemeId = 'drafting' | 'fieldnotes' | 'signal' | 'ink' | 'blueprint' | 'drafting-dark';
13
13
  export declare const THEME_IDS: ThemeId[];
14
14
  export declare const DEFAULT_THEME: ThemeId;
15
15
  /** Shade stops. Lower numbers are lighter and higher numbers darker, in every theme. */
@@ -154,6 +154,13 @@ interface Preset {
154
154
  hues?: Partial<Record<string, [number, number]>>;
155
155
  }
156
156
  export declare const PRESETS: Record<ThemeId, Preset>;
157
+ /**
158
+ * The appearance to draw on a page of this scheme. The default theme follows the page, with or
159
+ * without a `theme drafting` line and its size and font overrides: dark draws it as
160
+ * `drafting-dark`. Another named theme, a `theme-color` line or a color override on the `theme`
161
+ * line keeps the file's colors on every page.
162
+ */
163
+ export declare function forScheme(appearance: DiagramAppearance, dark: boolean): DiagramAppearance;
157
164
  /** `a` moved toward `b` by `t` (0–1), in sRGB. */
158
165
  export declare function mixColors(a: string, b: string, t: number): string;
159
166
  export declare function contrast(a: string, b: string): number;
@@ -0,0 +1,15 @@
1
+ /** Watches one host's scheme. `check` reads it again now; `stop` ends the watch. */
2
+ export interface SchemeWatch {
3
+ check(): void;
4
+ stop(): void;
5
+ }
6
+ /**
7
+ * Calls `onChange(dark)` now and whenever the scheme in `host` changes: an element, or a shadow
8
+ * root, whose children inherit the element's scheme. A hidden probe in `host` has a `light-dark()`
9
+ * color, so a scheme change anywhere above it (a class flip on the page, a style) starts a
10
+ * transition there; the media query covers a change of system setting under `light dark`. A
11
+ * transition cannot run while the host has no box (a hidden tab, a closed `<details>`, a detached
12
+ * mount), so a resize observer on the host reads the scheme again when it gets one. Callers also
13
+ * call `check` after each commit.
14
+ */
15
+ export declare function watchColorScheme(host: Element | ShadowRoot, onChange: (dark: boolean) => void): SchemeWatch;
@@ -3,6 +3,7 @@
3
3
  * packages loop over `HOST_EVENTS`, so a new event adds no lines to any of them.
4
4
  * This module has no side effects and no runtime imports.
5
5
  */
6
+ import type { RenderFailure } from './types';
6
7
  import type { UpdateResult, ViewerEvents } from './viewer';
7
8
  export type { Camera, CameraReason, FrameOptions, MoveResult } from './camera';
8
9
  export type { Box, EdgeOut, Geometry, PlacedBox } from './geometry';
@@ -10,8 +11,11 @@ export type { Anchor, DraftSlot } from './anchors';
10
11
  export interface HostEvents {
11
12
  /** After each commit, after the marked children change, and with `[]` before destroy. */
12
13
  anchors: ViewerEvents['anchors'];
13
- /** Each update's result: committed, error or a cancellation. */
14
- update: UpdateResult;
14
+ /**
15
+ * Each update's result, from the first source on: committed, error or a cancellation. The
16
+ * editor's commit has no `value` or `diagnostics` (see `EditorUpdate`).
17
+ */
18
+ update: UpdateResult | EditorUpdate;
15
19
  render: ViewerEvents['render'];
16
20
  diagnostics: ViewerEvents['diagnostics'];
17
21
  camera: ViewerEvents['camera'];
@@ -30,11 +34,39 @@ export interface HostEvents {
30
34
  source: string;
31
35
  };
32
36
  }
37
+ /**
38
+ * `<draft-editor>`'s update. The editor draws in its own session, not as one SVG, so a commit
39
+ * has no `value` and no `diagnostics`: the `diagnostics` event has them. A source that does not
40
+ * parse is an `error` with a `DRAFT_PARSE` diagnostic; the editor keeps the text and shows it.
41
+ */
42
+ export type EditorUpdate = {
43
+ status: 'committed';
44
+ revision: number;
45
+ value?: never;
46
+ diagnostics?: never;
47
+ } | {
48
+ status: 'error';
49
+ revision: number;
50
+ error: RenderFailure;
51
+ } | {
52
+ status: 'destroyed';
53
+ revision: number;
54
+ };
33
55
  export type HostEventName = keyof HostEvents;
34
56
  /** Every event, in one list. Each element dispatches these as bubbling `draft-<name>` events. */
35
57
  export declare const HOST_EVENTS: readonly ["anchors", "update", "render", "diagnostics", "camera", "itemactivate", "link", "contextmenu", "select", "change"];
36
58
  /** Events a host can cancel with `preventDefault()`. */
59
+ export type CancelableEventName = 'link' | 'contextmenu';
37
60
  export declare const CANCELABLE_EVENTS: readonly HostEventName[];
61
+ /**
62
+ * What a framework event prop receives: the event's `detail`, and on a cancelable event a
63
+ * `preventDefault()` that cancels the DOM event, so `onLink={e => e.preventDefault()}` works.
64
+ */
65
+ export type HostEventDetail<K extends HostEventName> = K extends CancelableEventName ? HostEvents[K] & {
66
+ preventDefault(): void;
67
+ } : HostEvents[K];
68
+ /** The value a framework event prop receives for a `draft-<name>` event. */
69
+ export declare function hostDetail(event: Event): unknown;
38
70
  export type DraftEventMap = {
39
71
  [K in HostEventName as `draft-${K}`]: CustomEvent<HostEvents[K]>;
40
72
  };
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import type { RenderOptions, RenderResult } from './types';
6
6
  /** Names the worker and the usual fix, so "worker failed" is not a dead end. */
7
- export declare const WORKER_HELP = "The worker is the module next to @draftably/runtime's viewer. With Vite, add optimizeDeps: { exclude: ['@draftably/runtime', '@draftably/editor'] } to vite.config.";
7
+ export declare const WORKER_HELP = "The worker is the module next to @draftably/runtime's viewer. With Vite 5\u20137, add optimizeDeps: { exclude: ['@draftably/runtime', '@draftably/editor'] } to vite.config.";
8
8
  /**
9
9
  * Render in the page's worker pool. Jobs queue on the main thread; an aborted job leaves the
10
10
  * queue before a worker sees it. A job that runs past `timeoutMs` stops its worker only.
@@ -0,0 +1,2 @@
1
+ /** Both drawings as one, or null when they differ in more than color. */
2
+ export declare function mergeSchemes(light: string, dark: string): string | null;
@@ -26,10 +26,15 @@ export interface SvgOptions {
26
26
  rects: Map<string, Rect>;
27
27
  routes: Route[];
28
28
  }) => void;
29
+ /**
30
+ * The routes `onDrawn` gave for this document, so drawing it again in other colors routes
31
+ * nothing again (not for compare or states).
32
+ */
33
+ routes?: Route[];
29
34
  }
30
35
  export declare class SvgLimitError extends Error {
31
36
  }
32
- type Route = ReturnType<typeof computeRoutes>['routes'][number];
37
+ export type Route = ReturnType<typeof computeRoutes>['routes'][number];
33
38
  /**
34
39
  * A sequence diagram's panel, a light surface with a border that bounds the exchange, and a row
35
40
  * band for each phase with its label in a tab at the left edge. Drawn under everything else.
@@ -43,4 +48,3 @@ export declare const PHASE_TAB = 8;
43
48
  /** A thin stripe across the top of a box, inside its rounded corners. */
44
49
  export declare function accentPath(r: Rect, radius: number): string;
45
50
  export declare function renderSvg(doc: Diagram, opts?: SvgOptions): string;
46
- export {};
@@ -49,6 +49,13 @@ export interface CommonRenderOptions {
49
49
  sourceId?: string;
50
50
  signal?: AbortSignal;
51
51
  limits?: Partial<RenderLimits>;
52
+ /**
53
+ * `light` (default) draws the file's own colors. `dark` draws a file without a `theme` line in
54
+ * the default theme's dark variant. `auto` writes both as `light-dark()` colors, so the page's
55
+ * `color-scheme` picks one; the viewer and `<draft-diagram>` use it. A file's own theme line
56
+ * always wins.
57
+ */
58
+ colorScheme?: 'light' | 'dark' | 'auto';
52
59
  }
53
60
  export type RenderOptions = CommonRenderOptions & ({
54
61
  target?: 'image';
@@ -269,6 +269,9 @@ export declare const setActiveSession: (session: DiagramSession) => {
269
269
  showState: (id: string | null) => void;
270
270
  stepState: (by: 1 | -1) => boolean;
271
271
  drawnDoc: () => import("../model/types").Diagram;
272
+ dark: import("solid-js").Accessor<boolean>;
273
+ setDark: import("solid-js").Setter<boolean>;
274
+ drawnAppearance: () => import("../model/appearance").DiagramAppearance;
272
275
  } & {
273
276
  tool: import("solid-js").Accessor<import("./session/editor").Tool>;
274
277
  setToolSignal: import("solid-js").Setter<import("./session/editor").Tool>;
@@ -7,5 +7,8 @@ export declare function createEmphasis(s: DocumentPart): {
7
7
  showState: (id: string | null) => void;
8
8
  stepState: (by: 1 | -1) => boolean;
9
9
  drawnDoc: () => import("../../model/types").Diagram;
10
+ dark: import("solid-js").Accessor<boolean>;
11
+ setDark: import("solid-js").Setter<boolean>;
12
+ drawnAppearance: () => import("../../model/appearance").DiagramAppearance;
10
13
  };
11
14
  export type EmphasisPart = ReturnType<typeof createEmphasis>;
@@ -15,6 +15,11 @@ export interface SessionOptions {
15
15
  openLink?: (href: string) => void;
16
16
  /** The user started a gesture or a tool on the canvas; a host closes its menus here. */
17
17
  onInteract?: () => void;
18
+ /** A link was pressed, before it opens. Return true to stop it opening. */
19
+ onLink?: (link: {
20
+ href: string;
21
+ itemId: string | undefined;
22
+ }) => boolean;
18
23
  }
19
24
  export declare function createDiagramSession(options?: SessionOptions): {
20
25
  dispose: () => void;
@@ -284,6 +289,9 @@ export declare function createDiagramSession(options?: SessionOptions): {
284
289
  showState: (id: string | null) => void;
285
290
  stepState: (by: 1 | -1) => boolean;
286
291
  drawnDoc: () => import("../../model/types").Diagram;
292
+ dark: import("solid-js").Accessor<boolean>;
293
+ setDark: import("solid-js").Setter<boolean>;
294
+ drawnAppearance: () => import("../../model/appearance").DiagramAppearance;
287
295
  } & {
288
296
  tool: import("solid-js").Accessor<import("./editor").Tool>;
289
297
  setToolSignal: import("solid-js").Setter<import("./editor").Tool>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@draftably/editor",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "The Draftably diagram editor: drag boxes and lines, and get the .draft source back.",
5
5
  "license": "MIT",
6
6
  "type": "module",