flowsvg 0.1.0 → 0.2.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 (98) hide show
  1. package/CHANGELOG.md +54 -1
  2. package/PATTERNS.md +71 -4
  3. package/README.md +148 -438
  4. package/README.zh-CN.md +148 -438
  5. package/dist/adapters/react.d.ts +55 -0
  6. package/dist/adapters/react.js +138 -0
  7. package/dist/adapters/vue.d.ts +158 -0
  8. package/dist/adapters/vue.js +177 -0
  9. package/dist/adapters/web-component.d.ts +111 -0
  10. package/dist/adapters/web-component.js +425 -0
  11. package/dist/{player-controls.d.ts → browser/controls.d.ts} +7 -3
  12. package/dist/{player-controls.js → browser/controls.js} +23 -31
  13. package/dist/browser/export-controls.d.ts +32 -0
  14. package/dist/browser/export-controls.js +151 -0
  15. package/dist/browser/player.d.ts +77 -0
  16. package/dist/browser/player.js +412 -0
  17. package/dist/browser/select-control.d.ts +33 -0
  18. package/dist/browser/select-control.js +171 -0
  19. package/dist/browser.d.ts +5 -69
  20. package/dist/browser.js +3 -353
  21. package/dist/core/color.d.ts +3 -0
  22. package/dist/core/color.js +18 -0
  23. package/dist/core/diagram.d.ts +40 -0
  24. package/dist/core/diagram.js +481 -0
  25. package/dist/core/edge-geometry.d.ts +45 -0
  26. package/dist/core/edge-geometry.js +609 -0
  27. package/dist/{core.d.ts → core/model.d.ts} +37 -61
  28. package/dist/core/model.js +1 -0
  29. package/dist/core/node-content-measurement.d.ts +34 -0
  30. package/dist/core/node-content-measurement.js +66 -0
  31. package/dist/{node-content-paint.d.ts → core/node-content-paint.d.ts} +2 -2
  32. package/dist/{node-content-paint.js → core/node-content-paint.js} +11 -20
  33. package/dist/{node-content.d.ts → core/node-content.d.ts} +18 -1
  34. package/dist/{node-content.js → core/node-content.js} +30 -6
  35. package/dist/core/svg-number.d.ts +5 -0
  36. package/dist/core/svg-number.js +7 -0
  37. package/dist/{theme.d.ts → core/theme.d.ts} +6 -0
  38. package/dist/core/theme.js +17 -0
  39. package/dist/{timeline.d.ts → core/timeline.d.ts} +11 -5
  40. package/dist/{timeline.js → core/timeline.js} +30 -22
  41. package/dist/core/validation.d.ts +11 -0
  42. package/dist/core/validation.js +399 -0
  43. package/dist/export/browser.d.ts +9 -0
  44. package/dist/export/browser.js +172 -0
  45. package/dist/export/captions.d.ts +18 -0
  46. package/dist/export/captions.js +44 -0
  47. package/dist/export/plan.d.ts +25 -0
  48. package/dist/export/plan.js +94 -0
  49. package/dist/export/types.d.ts +64 -0
  50. package/dist/export/types.js +9 -0
  51. package/dist/export/worker-source.d.ts +1 -0
  52. package/dist/export/worker-source.js +1 -0
  53. package/dist/export.d.ts +3 -0
  54. package/dist/export.js +2 -0
  55. package/dist/index.d.ts +7 -1
  56. package/dist/index.js +3 -1
  57. package/dist/node/cli.d.ts +2 -0
  58. package/dist/node/cli.js +72 -0
  59. package/dist/node/gif.d.ts +16 -0
  60. package/dist/node/gif.js +184 -0
  61. package/dist/node/html.d.ts +19 -0
  62. package/dist/node/html.js +61 -0
  63. package/dist/node/output.d.ts +2 -0
  64. package/dist/node/output.js +22 -0
  65. package/dist/node.d.ts +6 -0
  66. package/dist/node.js +3 -0
  67. package/dist/portable/LICENSE.esbuild.txt +21 -0
  68. package/dist/portable/LICENSE.flowsvg.txt +21 -0
  69. package/dist/portable/LICENSE.gifenc.txt +21 -0
  70. package/dist/portable/export.js +5 -0
  71. package/dist/portable/manifest.json +36 -0
  72. package/dist/portable/player.js +2 -0
  73. package/dist/portable/styles.css +227 -0
  74. package/dist/react.d.ts +3 -48
  75. package/dist/react.js +2 -126
  76. package/dist/styles.css +194 -14
  77. package/dist/vue.d.ts +3 -132
  78. package/dist/vue.js +2 -161
  79. package/dist/web-component.d.ts +3 -103
  80. package/dist/web-component.js +2 -396
  81. package/docs/images/entity-graph.png +0 -0
  82. package/docs/images/facts-timeline.png +0 -0
  83. package/docs/images/facts-timeline.zh-CN.png +0 -0
  84. package/docs/images/flowsvg-wordmark.gif +0 -0
  85. package/docs/images/node-content.png +0 -0
  86. package/docs/images/numbered-list.png +0 -0
  87. package/docs/images/numbered-list.zh-CN.png +0 -0
  88. package/docs/images/quick-start.png +0 -0
  89. package/docs/images/record-tags.png +0 -0
  90. package/package.json +38 -6
  91. package/dist/core.js +0 -1149
  92. package/dist/theme.js +0 -6
  93. /package/dist/{player-accessibility.d.ts → browser/accessibility.d.ts} +0 -0
  94. /package/dist/{player-accessibility.js → browser/accessibility.js} +0 -0
  95. /package/dist/{player-lifecycle.d.ts → browser/lifecycle.d.ts} +0 -0
  96. /package/dist/{player-lifecycle.js → browser/lifecycle.js} +0 -0
  97. /package/dist/{svg-dom.d.ts → browser/svg-dom.d.ts} +0 -0
  98. /package/dist/{svg-dom.js → browser/svg-dom.js} +0 -0
package/dist/styles.css CHANGED
@@ -1,4 +1,4 @@
1
- /* Control and canvas focus styles; CSS variables customize controls only. The core theme resolves diagram colors. */
1
+ /* CSS variables style playback and custom choices; core themes own diagram colors. */
2
2
  .flowsvg-canvas:focus-visible {
3
3
  outline: 3px solid #2563eb;
4
4
  outline-offset: 3px;
@@ -9,39 +9,219 @@
9
9
  flex-wrap: wrap;
10
10
  align-items: center;
11
11
  gap: 0.5rem;
12
- padding: 0.75rem;
12
+ padding: 0.875rem 1rem;
13
+ border-top: 1px solid var(--flowsvg-control-border, #e2e8f0);
13
14
  color: var(--flowsvg-control-color, #172033);
14
15
  background: var(--flowsvg-control-background, #ffffff);
15
- font: 1rem/1.4 system-ui, sans-serif;
16
+ font: 500 0.8125rem/1.4 system-ui, sans-serif;
17
+ font-variant-numeric: tabular-nums;
16
18
  }
17
19
 
18
- .flowsvg-controls :is(button, input, select) {
20
+ .flowsvg-controls :is(button, input) {
19
21
  box-sizing: border-box;
20
- min-height: 2.75rem;
22
+ min-height: var(--flowsvg-control-height, 2.75rem);
21
23
  max-width: 100%;
22
24
  color: var(--flowsvg-control-color, #172033);
23
25
  background: var(--flowsvg-control-background, #ffffff);
24
- border: 1px solid var(--flowsvg-control-border, #64748b);
25
- border-radius: 0.25rem;
26
+ border: 1px solid var(--flowsvg-control-border, #d8dee8);
27
+ border-radius: 0.5rem;
26
28
  font: inherit;
27
29
  accent-color: var(--flowsvg-control-accent, #2563eb);
30
+ transition: background-color 160ms ease, border-color 160ms ease;
28
31
  }
29
32
 
30
- .flowsvg-controls :is(button, select) {
31
- padding: 0.5rem 0.75rem;
33
+ .flowsvg-controls button {
34
+ padding: var(--flowsvg-control-padding, 0.5rem 0.75rem);
35
+ cursor: pointer;
32
36
  }
33
37
 
34
- .flowsvg-controls :is(button, input, select):focus-visible {
35
- outline: 3px solid var(--flowsvg-control-focus, #2563eb);
36
- outline-offset: 3px;
38
+ .flowsvg-controls button:enabled:hover,
39
+ .flowsvg-select-trigger:enabled:hover {
40
+ background: color-mix(in srgb, var(--flowsvg-control-color, #172033) 4%, var(--flowsvg-control-background, #ffffff));
41
+ border-color: color-mix(in srgb, var(--flowsvg-control-color, #172033) 25%, var(--flowsvg-control-border, #d8dee8));
42
+ }
43
+
44
+ .flowsvg-controls button:enabled:active,
45
+ .flowsvg-select-trigger:enabled:active {
46
+ background: color-mix(in srgb, var(--flowsvg-control-color, #172033) 8%, var(--flowsvg-control-background, #ffffff));
47
+ }
48
+
49
+ .flowsvg-controls .flowsvg-play-toggle {
50
+ min-width: 4.5rem;
51
+ color: var(--flowsvg-control-accent, #2563eb);
52
+ background: color-mix(in srgb, var(--flowsvg-control-accent, #2563eb) 10%, var(--flowsvg-control-background, #ffffff));
53
+ border-color: color-mix(in srgb, var(--flowsvg-control-accent, #2563eb) 35%, var(--flowsvg-control-border, #d8dee8));
54
+ font-weight: 650;
55
+ }
56
+
57
+ .flowsvg-controls .flowsvg-play-toggle:enabled:hover {
58
+ background: color-mix(in srgb, var(--flowsvg-control-accent, #2563eb) 17%, var(--flowsvg-control-background, #ffffff));
59
+ }
60
+
61
+ .flowsvg-controls .flowsvg-play-toggle:enabled:active {
62
+ background: color-mix(in srgb, var(--flowsvg-control-accent, #2563eb) 23%, var(--flowsvg-control-background, #ffffff));
63
+ }
64
+
65
+ .flowsvg-controls .flowsvg-loop-toggle[aria-pressed="true"] {
66
+ color: var(--flowsvg-control-accent, #2563eb);
67
+ background: color-mix(in srgb, var(--flowsvg-control-accent, #2563eb) 10%, var(--flowsvg-control-background, #ffffff));
68
+ border-color: var(--flowsvg-control-accent, #2563eb);
69
+ }
70
+
71
+ .flowsvg-controls :is(button, input):focus-visible {
72
+ outline: 2px solid var(--flowsvg-control-focus, #2563eb);
73
+ outline-offset: 2px;
37
74
  }
38
75
 
39
76
  .flowsvg-controls :disabled {
40
- opacity: 0.55;
41
- cursor: default;
77
+ opacity: 0.45;
78
+ cursor: not-allowed;
42
79
  }
43
80
 
44
81
  .flowsvg-controls input[type="range"] {
45
82
  flex: 1 1 10rem;
46
83
  min-width: 4rem;
84
+ margin: 0 0.375rem;
85
+ padding: 0;
86
+ border: 0;
87
+ border-radius: 0;
88
+ background: transparent;
89
+ cursor: pointer;
47
90
  }
91
+
92
+ .flowsvg-controls input[type="range"]:disabled {
93
+ cursor: not-allowed;
94
+ }
95
+
96
+ .flowsvg-export-controls {
97
+ display: inline-flex;
98
+ flex-wrap: wrap;
99
+ align-items: center;
100
+ gap: 0.5rem;
101
+ margin-inline-start: auto;
102
+ max-width: 100%;
103
+ }
104
+
105
+ .flowsvg-export-controls progress {
106
+ max-width: 7rem;
107
+ height: 0.375rem;
108
+ accent-color: var(--flowsvg-control-accent, #2563eb);
109
+ }
110
+
111
+ .flowsvg-export-controls [role="status"] {
112
+ font-size: 0.75rem;
113
+ }
114
+
115
+ .flowsvg-export-controls [role="status"]:empty {
116
+ display: none;
117
+ }
118
+
119
+ @media (max-width: 640px) {
120
+ .flowsvg-controls { padding: 0.75rem; }
121
+ .flowsvg-controls input[type="range"] { flex-basis: 100%; margin: 0; }
122
+ .flowsvg-export-controls { margin-inline-start: 0; }
123
+ }
124
+
125
+ @media (prefers-reduced-motion: reduce) {
126
+ .flowsvg-controls :is(button, input) { transition: none; }
127
+ }
128
+
129
+ /* Apply to a host or ancestor; sizing affects controls only, never diagram geometry. */
130
+ .flowsvg-compact {
131
+ --flowsvg-control-height: 2rem;
132
+ --flowsvg-control-padding: 0.3125rem 0.625rem;
133
+ }
134
+
135
+ .flowsvg-select {
136
+ position: relative;
137
+ display: inline-flex;
138
+ max-width: 100%;
139
+ color: var(--flowsvg-control-color, #172033);
140
+ font: 500 0.8125rem/1.4 system-ui, sans-serif;
141
+ font-variant-numeric: tabular-nums;
142
+ }
143
+
144
+ .flowsvg-select .flowsvg-select-trigger {
145
+ box-sizing: border-box;
146
+ display: inline-flex;
147
+ align-items: center;
148
+ justify-content: space-between;
149
+ gap: 1rem;
150
+ width: 100%;
151
+ min-height: var(--flowsvg-control-height, 2.75rem);
152
+ padding: var(--flowsvg-control-padding, 0.5rem 0.75rem);
153
+ color: inherit;
154
+ background: var(--flowsvg-control-background, #ffffff);
155
+ border: 1px solid var(--flowsvg-control-border, #d8dee8);
156
+ border-radius: 0.5rem;
157
+ font: inherit;
158
+ text-align: start;
159
+ cursor: pointer;
160
+ }
161
+
162
+ .flowsvg-select-trigger::after {
163
+ content: "";
164
+ width: 0.375rem;
165
+ height: 0.375rem;
166
+ margin: -0.1875rem 0.125rem 0 0;
167
+ border-right: 1.5px solid currentColor;
168
+ border-bottom: 1.5px solid currentColor;
169
+ transform: rotate(45deg);
170
+ }
171
+
172
+ .flowsvg-select-trigger:focus-visible {
173
+ outline: 2px solid var(--flowsvg-control-focus, #2563eb);
174
+ outline-offset: 2px;
175
+ }
176
+
177
+ .flowsvg-select-trigger:disabled { opacity: 0.45; cursor: not-allowed; }
178
+
179
+ .flowsvg-select-options {
180
+ box-sizing: border-box;
181
+ position: absolute;
182
+ z-index: 2;
183
+ inset-block-start: calc(100% + 0.375rem);
184
+ inset-inline-end: 0;
185
+ width: max-content;
186
+ min-width: 100%;
187
+ max-width: min(22rem, calc(100vw - 2.5rem));
188
+ max-height: 14rem;
189
+ overflow-y: auto;
190
+ overscroll-behavior: contain;
191
+ padding: 0.25rem;
192
+ color: var(--flowsvg-control-color, #172033);
193
+ background: var(--flowsvg-control-background, #ffffff);
194
+ border: 1px solid var(--flowsvg-control-border, #d8dee8);
195
+ border-radius: 0.625rem;
196
+ box-shadow: 0 8px 24px color-mix(in srgb, var(--flowsvg-control-color, #172033) 14%, transparent);
197
+ }
198
+
199
+ .flowsvg-controls .flowsvg-select-options {
200
+ inset-block-start: auto;
201
+ inset-block-end: calc(100% + 0.375rem);
202
+ }
203
+
204
+ .flowsvg-select-options [role="option"] {
205
+ position: relative;
206
+ padding: 0.5rem 1.875rem 0.5rem 0.625rem;
207
+ border-radius: 0.375rem;
208
+ overflow-wrap: anywhere;
209
+ cursor: pointer;
210
+ }
211
+
212
+ .flowsvg-select-options [role="option"]:is(:hover, [data-active]) {
213
+ background: color-mix(in srgb, var(--flowsvg-control-accent, #2563eb) 9%, var(--flowsvg-control-background, #ffffff));
214
+ }
215
+
216
+ .flowsvg-select-options [aria-selected="true"] {
217
+ color: var(--flowsvg-control-accent, #2563eb);
218
+ font-weight: 650;
219
+ }
220
+
221
+ .flowsvg-select-options [aria-selected="true"]::after {
222
+ content: "✓";
223
+ position: absolute;
224
+ inset-inline-end: 0.625rem;
225
+ }
226
+
227
+ .flowsvg-compact .flowsvg-play-toggle { min-width: 4rem; }
package/dist/vue.d.ts CHANGED
@@ -1,132 +1,3 @@
1
- /**
2
- * Vue adapter for component lifecycle, props, events and imperative controls.
3
- * Playback/rendering reuse browser; imports do not create DOM, and the player is created only on mount.
4
- */
5
- import { type CSSProperties, type PropType } from "vue";
6
- import { type PlayerErrorDetail, type PlayerState } from "./browser.js";
7
- import type { DiagramDocument, ThemeName } from "./core.js";
8
- /** Vue document, visual options and callbacks; document reference changes trigger a reload. */
9
- export interface FlowPlayerProps {
10
- document: DiagramDocument;
11
- /** Positive playback multiplier; omit to use the player default. */
12
- speed?: number;
13
- theme?: ThemeName;
14
- controls?: boolean;
15
- /** Defaults to false: hover marks borders only. True also highlights backgrounds, including groups. */
16
- hoverFill?: boolean;
17
- reducedMotion?: boolean;
18
- /** Initial position in milliseconds, applied once; use seek afterward, and document reloads reset to zero. */
19
- initialTimeMs?: number;
20
- className?: string;
21
- style?: CSSProperties;
22
- onStepChange?: (state: PlayerState) => void;
23
- onTimeUpdate?: (state: PlayerState) => void;
24
- onPlayStateChange?: (state: PlayerState) => void;
25
- onError?: (detail: PlayerErrorDetail) => void;
26
- }
27
- /** Imperative controls for a mounted component; calls before mounting or after unmounting throw a not-mounted error. */
28
- export interface FlowPlayerHandle {
29
- /** Resume from the current position when playback can advance. */
30
- play(): void;
31
- /** Pause while retaining the current frame. */
32
- pause(): void;
33
- /** Seek in milliseconds, clamping to the timeline; non-finite values become zero. */
34
- seek(timeMs: number): void;
35
- /** Pause and jump to a beat boundary; backward from inside a beat goes to that beat's start. */
36
- step(direction: 1 | -1): void;
37
- /** Set a finite positive multiplier; ignore invalid values. */
38
- setSpeed(speed: number): void;
39
- /** Reset/pause at zero on success; emit an error and retain playback state if validation fails. */
40
- load(document: DiagramDocument): void;
41
- /** Read the current browser-player snapshot. */
42
- getState(): PlayerState;
43
- }
44
- /**
45
- * SSR renders only the host; onMounted creates the player and onBeforeUnmount releases it.
46
- * Document reference changes call load; option changes rebuild, and exposed methods delegate to the browser instance.
47
- */
48
- export declare const FlowPlayer: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
49
- readonly document: {
50
- readonly type: PropType<DiagramDocument>;
51
- readonly required: true;
52
- };
53
- readonly speed: {
54
- readonly type: NumberConstructor;
55
- readonly required: false;
56
- };
57
- readonly theme: {
58
- readonly type: PropType<ThemeName>;
59
- readonly required: false;
60
- };
61
- readonly controls: {
62
- readonly type: PropType<boolean | undefined>;
63
- readonly default: undefined;
64
- };
65
- readonly hoverFill: {
66
- readonly type: PropType<boolean | undefined>;
67
- readonly default: undefined;
68
- };
69
- readonly reducedMotion: {
70
- readonly type: PropType<boolean | undefined>;
71
- readonly default: undefined;
72
- };
73
- readonly initialTimeMs: {
74
- readonly type: NumberConstructor;
75
- readonly required: false;
76
- };
77
- readonly className: {
78
- readonly type: StringConstructor;
79
- readonly required: false;
80
- };
81
- readonly style: {
82
- readonly type: PropType<CSSProperties>;
83
- readonly required: false;
84
- };
85
- readonly onStepChange: {
86
- readonly type: PropType<(state: PlayerState) => void>;
87
- readonly required: false;
88
- };
89
- readonly onTimeUpdate: {
90
- readonly type: PropType<(state: PlayerState) => void>;
91
- readonly required: false;
92
- };
93
- readonly onPlayStateChange: {
94
- readonly type: PropType<(state: PlayerState) => void>;
95
- readonly required: false;
96
- };
97
- readonly onError: {
98
- readonly type: PropType<(detail: PlayerErrorDetail) => void>;
99
- readonly required: false;
100
- };
101
- }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
102
- [key: string]: any;
103
- }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
104
- stepchange: (_state: PlayerState) => true;
105
- timeupdate: (_state: PlayerState) => true;
106
- playstatechange: (_state: PlayerState) => true;
107
- error: (_detail: PlayerErrorDetail) => true;
108
- }, string, import("vue").PublicProps, Readonly<{
109
- readonly document: DiagramDocument;
110
- } & {
111
- readonly theme?: ThemeName | undefined;
112
- readonly style?: CSSProperties | undefined;
113
- readonly controls?: boolean | undefined;
114
- readonly hoverFill?: boolean | undefined;
115
- readonly speed?: number | undefined;
116
- readonly reducedMotion?: boolean | undefined;
117
- readonly initialTimeMs?: number | undefined;
118
- readonly className?: string | undefined;
119
- readonly onStepChange?: ((state: PlayerState) => void) | undefined;
120
- readonly onTimeUpdate?: ((state: PlayerState) => void) | undefined;
121
- readonly onPlayStateChange?: ((state: PlayerState) => void) | undefined;
122
- readonly onError?: ((detail: PlayerErrorDetail) => void) | undefined;
123
- } & {
124
- onError?: ((_detail: PlayerErrorDetail) => any) | undefined;
125
- onTimeupdate?: ((_state: PlayerState) => any) | undefined;
126
- onPlaystatechange?: ((_state: PlayerState) => any) | undefined;
127
- onStepchange?: ((_state: PlayerState) => any) | undefined;
128
- }>, {
129
- readonly controls: boolean | undefined;
130
- readonly hoverFill: boolean | undefined;
131
- readonly reducedMotion: boolean | undefined;
132
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}>;
1
+ /** Public Vue adapter entry. */
2
+ export { FlowPlayer } from "./adapters/vue.js";
3
+ export type { FlowPlayerHandle, FlowPlayerProps } from "./adapters/vue.js";
package/dist/vue.js CHANGED
@@ -1,161 +1,2 @@
1
- /**
2
- * Vue adapter for component lifecycle, props, events and imperative controls.
3
- * Playback/rendering reuse browser; imports do not create DOM, and the player is created only on mount.
4
- */
5
- import { defineComponent, h, onBeforeUnmount, onMounted, ref, watch } from "vue";
6
- import { createPlayer } from "./browser.js";
7
- const propsOptions = {
8
- document: { type: Object, required: true },
9
- speed: { type: Number, required: false },
10
- theme: { type: String, required: false },
11
- controls: { type: Boolean, default: undefined },
12
- hoverFill: { type: Boolean, default: undefined },
13
- reducedMotion: { type: Boolean, default: undefined },
14
- initialTimeMs: { type: Number, required: false },
15
- className: { type: String, required: false },
16
- style: { type: Object, required: false },
17
- onStepChange: { type: Function, required: false },
18
- onTimeUpdate: { type: Function, required: false },
19
- onPlayStateChange: { type: Function, required: false },
20
- onError: { type: Function, required: false }
21
- };
22
- /**
23
- * SSR renders only the host; onMounted creates the player and onBeforeUnmount releases it.
24
- * Document reference changes call load; option changes rebuild, and exposed methods delegate to the browser instance.
25
- */
26
- export const FlowPlayer = defineComponent({
27
- name: "FlowPlayer",
28
- inheritAttrs: false,
29
- props: propsOptions,
30
- emits: {
31
- stepchange: (_state) => true,
32
- timeupdate: (_state) => true,
33
- playstatechange: (_state) => true,
34
- error: (_detail) => true
35
- },
36
- setup(props, { attrs, emit, expose }) {
37
- const hostRef = ref(null);
38
- let player;
39
- let cleanupEvents;
40
- let optionsSnapshot;
41
- let mounted = false;
42
- let initialApplied = false;
43
- const requirePlayer = () => {
44
- if (!player)
45
- throw new Error("FlowPlayer is not mounted");
46
- return player;
47
- };
48
- expose({
49
- play: () => requirePlayer().play(),
50
- pause: () => requirePlayer().pause(),
51
- seek: (timeMs) => requirePlayer().seek(timeMs),
52
- step: (direction) => requirePlayer().step(direction),
53
- setSpeed: (speed) => requirePlayer().setSpeed(speed),
54
- load: (document) => requirePlayer().load(document),
55
- getState: () => requirePlayer().getState()
56
- });
57
- const currentOptions = () => ({
58
- theme: props.theme,
59
- controls: props.controls,
60
- hoverFill: props.hoverFill,
61
- reducedMotion: props.reducedMotion
62
- });
63
- const attachEvents = (next) => {
64
- const onStepChange = (event) => {
65
- const state = event.detail;
66
- emit("stepchange", state);
67
- props.onStepChange?.(state);
68
- };
69
- const onTimeUpdate = (event) => {
70
- const state = event.detail;
71
- emit("timeupdate", state);
72
- props.onTimeUpdate?.(state);
73
- };
74
- const onPlayStateChange = (event) => {
75
- const state = event.detail;
76
- emit("playstatechange", state);
77
- props.onPlayStateChange?.(state);
78
- };
79
- const onError = (event) => {
80
- const detail = event.detail;
81
- // Vue's error event already invokes the onError prop; an explicit callback would duplicate delivery.
82
- emit("error", detail);
83
- };
84
- next.element.addEventListener("stepchange", onStepChange);
85
- next.element.addEventListener("timeupdate", onTimeUpdate);
86
- next.element.addEventListener("playstatechange", onPlayStateChange);
87
- next.element.addEventListener("error", onError);
88
- return () => {
89
- next.element.removeEventListener("stepchange", onStepChange);
90
- next.element.removeEventListener("timeupdate", onTimeUpdate);
91
- next.element.removeEventListener("playstatechange", onPlayStateChange);
92
- next.element.removeEventListener("error", onError);
93
- };
94
- };
95
- // Keep initialApplied across option rebuilds so initialTimeMs is not reapplied.
96
- const mount = () => {
97
- const host = hostRef.value;
98
- if (!host || player)
99
- return;
100
- const next = createPlayer(host, props.document, currentOptions());
101
- player = next;
102
- optionsSnapshot = currentOptions();
103
- cleanupEvents = attachEvents(next);
104
- if (props.speed !== undefined)
105
- next.setSpeed(props.speed);
106
- if (!initialApplied && props.initialTimeMs !== undefined)
107
- next.seek(props.initialTimeMs);
108
- initialApplied = true;
109
- };
110
- // Detach event forwarding first so destruction does not call an unmounted component.
111
- const unmount = () => {
112
- cleanupEvents?.();
113
- cleanupEvents = undefined;
114
- player?.destroy();
115
- player = undefined;
116
- optionsSnapshot = undefined;
117
- };
118
- const rebuild = () => {
119
- if (!mounted || !player)
120
- return;
121
- unmount();
122
- mount();
123
- };
124
- onMounted(() => {
125
- mounted = true;
126
- mount();
127
- });
128
- watch(() => props.document, (next, previous) => {
129
- if (!mounted || next === previous || !player)
130
- return;
131
- player.load(next);
132
- });
133
- watch(() => [props.theme, props.controls, props.reducedMotion, props.hoverFill], () => {
134
- const next = currentOptions();
135
- if (!mounted || sameOptions(optionsSnapshot, next))
136
- return;
137
- rebuild();
138
- });
139
- watch(() => props.speed, (speed) => {
140
- if (player && speed !== undefined)
141
- player.setSpeed(speed);
142
- });
143
- onBeforeUnmount(() => {
144
- mounted = false;
145
- unmount();
146
- });
147
- return () => h("div", {
148
- ref: hostRef,
149
- class: ["flowsvg", props.className, attrs.class],
150
- style: [props.style, attrs.style]
151
- });
152
- }
153
- });
154
- function sameOptions(previous, next) {
155
- if (!previous)
156
- return false;
157
- return previous.theme === next.theme
158
- && previous.controls === next.controls
159
- && previous.hoverFill === next.hoverFill
160
- && previous.reducedMotion === next.reducedMotion;
161
- }
1
+ /** Public Vue adapter entry. */
2
+ export { FlowPlayer } from "./adapters/vue.js";
@@ -1,103 +1,3 @@
1
- /**
2
- * Web Component boundary: convert attributes/objects/URLs and manage lifecycle while reusing browser playback/rendering.
3
- * Imports do not register the element; DOM-free imports use a placeholder base, so real registration needs a browser load.
4
- */
5
- import { type DiagramDocument, type EvaluateOptions, type ThemeName } from "./core.js";
6
- import { type PlayerErrorDetail, type PlayerState } from "./browser.js";
7
- /** Default custom-element name registered by {@link defineFlowPlayer}. */
8
- export declare const FLOW_PLAYER_TAG = "flowsvg-player";
9
- type ElementBase = typeof HTMLElement;
10
- declare const HTMLElementBase: ElementBase;
11
- /** Declarative options; autoplay defaults to false, speed to 1 and controls to true. */
12
- export interface FlowPlayerElementOptions extends EvaluateOptions {
13
- autoplay?: boolean;
14
- controls?: boolean;
15
- /** Defaults to false; true enables background feedback as well as borders on hover. */
16
- hoverFill?: boolean;
17
- speed?: number;
18
- }
19
- /** Pre-mount errors may lack player state; mounted errors include available state and the Error. */
20
- export type FlowPlayerElementErrorDetail = Partial<PlayerErrorDetail> & {
21
- error: Error;
22
- };
23
- /**
24
- * Custom-element adapter for attribute conversion, URL requests and the four playback/error events.
25
- * The document property may be assigned before connection; disconnect releases the player/requests but retains the document.
26
- * Rendering/timeline behavior delegates to createPlayer; registration requires an explicit defineFlowPlayer call.
27
- */
28
- export declare class FlowPlayerElement extends HTMLElementBase {
29
- static readonly observedAttributes: readonly ["document", "src", "autoplay", "controls", "speed", "theme", "reduced-motion", "hover-fill"];
30
- private player;
31
- private playerHost;
32
- private sourceAbort;
33
- private requestToken;
34
- private connected;
35
- private manuallyDestroyed;
36
- private currentDocument;
37
- private controlsOverride;
38
- private readonly forwardedEvents;
39
- /** Current object document; assigning undefined clears it, cancels requests and unmounts the player. */
40
- get document(): DiagramDocument | undefined;
41
- set document(value: DiagramDocument | undefined);
42
- /** Document URL resolved against ownerDocument.baseURI; changes cancel obsolete loads. */
43
- get src(): string | undefined;
44
- set src(value: string | undefined);
45
- /** Whether to autoplay; false pauses a mounted player. */
46
- get autoplay(): boolean;
47
- set autoplay(value: boolean);
48
- /** Controls default on; an explicit false is retained in HTML as controls="false". */
49
- get controls(): boolean;
50
- set controls(value: boolean);
51
- /** Positive speed multiplier; invalid attribute text reads as 1, while invalid property assignments are ignored. */
52
- get speed(): number;
53
- set speed(value: number);
54
- /** Explicit light/dark theme; the player falls back to the document theme or light when unset. */
55
- get theme(): ThemeName | undefined;
56
- set theme(value: ThemeName | undefined);
57
- /** undefined follows the system preference; true/false explicitly override it via reduced-motion. */
58
- get reducedMotion(): boolean | undefined;
59
- set reducedMotion(value: boolean | undefined);
60
- /** Hover paints only borders by default. The hover-fill attribute enables background and content feedback. */
61
- get hoverFill(): boolean;
62
- set hoverFill(value: boolean);
63
- connectedCallback(): void;
64
- disconnectedCallback(): void;
65
- attributeChangedCallback(name: string, _oldValue: string | null, value: string | null): void;
66
- /** Forward controls to the mounted player; calls are inert when no player is mounted. */
67
- play(): void;
68
- /** Pause the mounted player, retaining its current frame; do nothing before mounting. */
69
- pause(): void;
70
- /** Seek in milliseconds using browser-player clamping; do nothing before mounting. */
71
- seek(timeMs: number): void;
72
- /** Pause and jump to a beat boundary; backward from inside a beat goes to that beat's start. */
73
- step(direction: 1 | -1): void;
74
- /** Set the speed property, retaining it for future mounts; ignore non-finite or non-positive values. */
75
- setSpeed(value: number): void;
76
- /** Return undefined before mounting or after destruction. */
77
- getState(): PlayerState | undefined;
78
- /** Explicitly release the player and requests; the manual-destruction flag resets on the next connection. */
79
- destroy(): void;
80
- private setDocument;
81
- private loadDocumentAttribute;
82
- /** AbortController cancels network work; the token also rejects obsolete results already received but not yet applied. */
83
- private loadSource;
84
- /** Restore an existing playback state during option rebuilds; autoplay applies only to a fresh mount. */
85
- private mountPlayer;
86
- /** Rebuild for option changes and restore time/playback; document replacement instead follows load's reset-to-zero rule. */
87
- private rebuildForOptions;
88
- /** Detach forwarding before destruction so teardown cannot forward events from the old instance. */
89
- private unmountPlayer;
90
- private forwardPlayerEvents;
91
- private removeForwardedEvents;
92
- private playerOptions;
93
- private applyAutoplay;
94
- private cancelSourceLoad;
95
- private dispatchError;
96
- }
97
- /**
98
- * Register explicitly; repeated calls for the default name return the existing constructor, or undefined without customElements.
99
- * Load the module with a DOM for real registration, since the HTMLElement base is fixed at module evaluation.
100
- * @throws {DOMException} From the registry for invalid names or reusing the constructor under another name.
101
- */
102
- export declare function defineFlowPlayer(tagName?: string): CustomElementConstructor | undefined;
103
- export {};
1
+ /** Public Web Component entry. */
2
+ export { FLOW_PLAYER_TAG, FlowPlayerElement, defineFlowPlayer } from "./adapters/web-component.js";
3
+ export type { FlowPlayerElementErrorDetail, FlowPlayerElementOptions } from "./adapters/web-component.js";