@sealpixel/ui 1.0.0-rc.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 (196) hide show
  1. package/LICENSE.md +59 -0
  2. package/README.md +10 -0
  3. package/dist/components/lumbra-button.d.ts +28 -0
  4. package/dist/components/lumbra-button.d.ts.map +1 -0
  5. package/dist/components/lumbra-button.js +87 -0
  6. package/dist/components/lumbra-button.js.map +1 -0
  7. package/dist/components/lumbra-editor.d.ts +226 -0
  8. package/dist/components/lumbra-editor.d.ts.map +1 -0
  9. package/dist/components/lumbra-editor.js +873 -0
  10. package/dist/components/lumbra-editor.js.map +1 -0
  11. package/dist/components/lumbra-nav.d.ts +48 -0
  12. package/dist/components/lumbra-nav.d.ts.map +1 -0
  13. package/dist/components/lumbra-nav.js +159 -0
  14. package/dist/components/lumbra-nav.js.map +1 -0
  15. package/dist/components/lumbra-slider.d.ts +48 -0
  16. package/dist/components/lumbra-slider.d.ts.map +1 -0
  17. package/dist/components/lumbra-slider.js +168 -0
  18. package/dist/components/lumbra-slider.js.map +1 -0
  19. package/dist/components/lumbra-stage.d.ts +92 -0
  20. package/dist/components/lumbra-stage.d.ts.map +1 -0
  21. package/dist/components/lumbra-stage.js +344 -0
  22. package/dist/components/lumbra-stage.js.map +1 -0
  23. package/dist/components/lumbra-toolbar.d.ts +30 -0
  24. package/dist/components/lumbra-toolbar.d.ts.map +1 -0
  25. package/dist/components/lumbra-toolbar.js +127 -0
  26. package/dist/components/lumbra-toolbar.js.map +1 -0
  27. package/dist/components/pincel-button.d.ts +28 -0
  28. package/dist/components/pincel-button.d.ts.map +1 -0
  29. package/dist/components/pincel-button.js +87 -0
  30. package/dist/components/pincel-button.js.map +1 -0
  31. package/dist/components/pincel-editor.d.ts +195 -0
  32. package/dist/components/pincel-editor.d.ts.map +1 -0
  33. package/dist/components/pincel-editor.js +794 -0
  34. package/dist/components/pincel-editor.js.map +1 -0
  35. package/dist/components/pincel-nav.d.ts +33 -0
  36. package/dist/components/pincel-nav.d.ts.map +1 -0
  37. package/dist/components/pincel-nav.js +147 -0
  38. package/dist/components/pincel-nav.js.map +1 -0
  39. package/dist/components/pincel-slider.d.ts +48 -0
  40. package/dist/components/pincel-slider.d.ts.map +1 -0
  41. package/dist/components/pincel-slider.js +168 -0
  42. package/dist/components/pincel-slider.js.map +1 -0
  43. package/dist/components/pincel-stage.d.ts +66 -0
  44. package/dist/components/pincel-stage.d.ts.map +1 -0
  45. package/dist/components/pincel-stage.js +233 -0
  46. package/dist/components/pincel-stage.js.map +1 -0
  47. package/dist/components/pincel-toolbar.d.ts +30 -0
  48. package/dist/components/pincel-toolbar.d.ts.map +1 -0
  49. package/dist/components/pincel-toolbar.js +127 -0
  50. package/dist/components/pincel-toolbar.js.map +1 -0
  51. package/dist/components/sealpixel-button.d.ts +28 -0
  52. package/dist/components/sealpixel-button.d.ts.map +1 -0
  53. package/dist/components/sealpixel-button.js +117 -0
  54. package/dist/components/sealpixel-button.js.map +1 -0
  55. package/dist/components/sealpixel-editor.d.ts +226 -0
  56. package/dist/components/sealpixel-editor.d.ts.map +1 -0
  57. package/dist/components/sealpixel-editor.js +1245 -0
  58. package/dist/components/sealpixel-editor.js.map +1 -0
  59. package/dist/components/sealpixel-nav.d.ts +48 -0
  60. package/dist/components/sealpixel-nav.d.ts.map +1 -0
  61. package/dist/components/sealpixel-nav.js +159 -0
  62. package/dist/components/sealpixel-nav.js.map +1 -0
  63. package/dist/components/sealpixel-slider.d.ts +75 -0
  64. package/dist/components/sealpixel-slider.d.ts.map +1 -0
  65. package/dist/components/sealpixel-slider.js +269 -0
  66. package/dist/components/sealpixel-slider.js.map +1 -0
  67. package/dist/components/sealpixel-stage.d.ts +118 -0
  68. package/dist/components/sealpixel-stage.d.ts.map +1 -0
  69. package/dist/components/sealpixel-stage.js +409 -0
  70. package/dist/components/sealpixel-stage.js.map +1 -0
  71. package/dist/components/sealpixel-stepper.d.ts +69 -0
  72. package/dist/components/sealpixel-stepper.d.ts.map +1 -0
  73. package/dist/components/sealpixel-stepper.js +248 -0
  74. package/dist/components/sealpixel-stepper.js.map +1 -0
  75. package/dist/components/sealpixel-toolbar.d.ts +30 -0
  76. package/dist/components/sealpixel-toolbar.d.ts.map +1 -0
  77. package/dist/components/sealpixel-toolbar.js +149 -0
  78. package/dist/components/sealpixel-toolbar.js.map +1 -0
  79. package/dist/controllers/gesture-controller.d.ts +65 -0
  80. package/dist/controllers/gesture-controller.d.ts.map +1 -0
  81. package/dist/controllers/gesture-controller.js +125 -0
  82. package/dist/controllers/gesture-controller.js.map +1 -0
  83. package/dist/controllers/session-controller.d.ts +15 -0
  84. package/dist/controllers/session-controller.d.ts.map +1 -0
  85. package/dist/controllers/session-controller.js +37 -0
  86. package/dist/controllers/session-controller.js.map +1 -0
  87. package/dist/define.d.ts +21 -0
  88. package/dist/define.d.ts.map +1 -0
  89. package/dist/define.js +27 -0
  90. package/dist/define.js.map +1 -0
  91. package/dist/i18n.d.ts +11 -0
  92. package/dist/i18n.d.ts.map +1 -0
  93. package/dist/i18n.js +27 -0
  94. package/dist/i18n.js.map +1 -0
  95. package/dist/icons.d.ts +32 -0
  96. package/dist/icons.d.ts.map +1 -0
  97. package/dist/icons.js +45 -0
  98. package/dist/icons.js.map +1 -0
  99. package/dist/index.d.ts +18 -0
  100. package/dist/index.d.ts.map +1 -0
  101. package/dist/index.js +17 -0
  102. package/dist/index.js.map +1 -0
  103. package/dist/locales/de.d.ts +3 -0
  104. package/dist/locales/de.d.ts.map +1 -0
  105. package/dist/locales/de.js +132 -0
  106. package/dist/locales/de.js.map +1 -0
  107. package/dist/locales/en.d.ts +3 -0
  108. package/dist/locales/en.d.ts.map +1 -0
  109. package/dist/locales/en.js +132 -0
  110. package/dist/locales/en.js.map +1 -0
  111. package/dist/locales/es.d.ts +3 -0
  112. package/dist/locales/es.d.ts.map +1 -0
  113. package/dist/locales/es.js +132 -0
  114. package/dist/locales/es.js.map +1 -0
  115. package/dist/locales/fr.d.ts +3 -0
  116. package/dist/locales/fr.d.ts.map +1 -0
  117. package/dist/locales/fr.js +132 -0
  118. package/dist/locales/fr.js.map +1 -0
  119. package/dist/locales/index.d.ts +6 -0
  120. package/dist/locales/index.d.ts.map +1 -0
  121. package/dist/locales/index.js +13 -0
  122. package/dist/locales/index.js.map +1 -0
  123. package/dist/locales/ja.d.ts +3 -0
  124. package/dist/locales/ja.d.ts.map +1 -0
  125. package/dist/locales/ja.js +132 -0
  126. package/dist/locales/ja.js.map +1 -0
  127. package/dist/locales/pt.d.ts +3 -0
  128. package/dist/locales/pt.d.ts.map +1 -0
  129. package/dist/locales/pt.js +132 -0
  130. package/dist/locales/pt.js.map +1 -0
  131. package/dist/theme.d.ts +19 -0
  132. package/dist/theme.d.ts.map +1 -0
  133. package/dist/theme.js +150 -0
  134. package/dist/theme.js.map +1 -0
  135. package/dist/tool.d.ts +70 -0
  136. package/dist/tool.d.ts.map +1 -0
  137. package/dist/tool.js +2 -0
  138. package/dist/tool.js.map +1 -0
  139. package/dist/tools/annotate.d.ts +3 -0
  140. package/dist/tools/annotate.d.ts.map +1 -0
  141. package/dist/tools/annotate.js +436 -0
  142. package/dist/tools/annotate.js.map +1 -0
  143. package/dist/tools/crop-mask.d.ts +29 -0
  144. package/dist/tools/crop-mask.d.ts.map +1 -0
  145. package/dist/tools/crop-mask.js +75 -0
  146. package/dist/tools/crop-mask.js.map +1 -0
  147. package/dist/tools/crop.d.ts +11 -0
  148. package/dist/tools/crop.d.ts.map +1 -0
  149. package/dist/tools/crop.js +361 -0
  150. package/dist/tools/crop.js.map +1 -0
  151. package/dist/tools/delete-handle.d.ts +18 -0
  152. package/dist/tools/delete-handle.d.ts.map +1 -0
  153. package/dist/tools/delete-handle.js +61 -0
  154. package/dist/tools/delete-handle.js.map +1 -0
  155. package/dist/tools/filter.d.ts +3 -0
  156. package/dist/tools/filter.d.ts.map +1 -0
  157. package/dist/tools/filter.js +174 -0
  158. package/dist/tools/filter.js.map +1 -0
  159. package/dist/tools/finetune.d.ts +11 -0
  160. package/dist/tools/finetune.d.ts.map +1 -0
  161. package/dist/tools/finetune.js +98 -0
  162. package/dist/tools/finetune.js.map +1 -0
  163. package/dist/tools/frame.d.ts +3 -0
  164. package/dist/tools/frame.d.ts.map +1 -0
  165. package/dist/tools/frame.js +57 -0
  166. package/dist/tools/frame.js.map +1 -0
  167. package/dist/tools/index.d.ts +14 -0
  168. package/dist/tools/index.d.ts.map +1 -0
  169. package/dist/tools/index.js +41 -0
  170. package/dist/tools/index.js.map +1 -0
  171. package/dist/tools/redact.d.ts +3 -0
  172. package/dist/tools/redact.d.ts.map +1 -0
  173. package/dist/tools/redact.js +252 -0
  174. package/dist/tools/redact.js.map +1 -0
  175. package/dist/tools/resize.d.ts +3 -0
  176. package/dist/tools/resize.d.ts.map +1 -0
  177. package/dist/tools/resize.js +91 -0
  178. package/dist/tools/resize.js.map +1 -0
  179. package/dist/tools/sticker.d.ts +8 -0
  180. package/dist/tools/sticker.d.ts.map +1 -0
  181. package/dist/tools/sticker.js +156 -0
  182. package/dist/tools/sticker.js.map +1 -0
  183. package/dist/tools/watermark.d.ts +3 -0
  184. package/dist/tools/watermark.d.ts.map +1 -0
  185. package/dist/tools/watermark.js +102 -0
  186. package/dist/tools/watermark.js.map +1 -0
  187. package/dist/view-mapping.d.ts +23 -0
  188. package/dist/view-mapping.d.ts.map +1 -0
  189. package/dist/view-mapping.js +72 -0
  190. package/dist/view-mapping.js.map +1 -0
  191. package/package.json +55 -0
  192. package/themes/default.css +23 -0
  193. package/themes/high-contrast.css +30 -0
  194. package/themes/minimal.css +32 -0
  195. package/themes/nocturne.css +21 -0
  196. package/themes/studio.css +23 -0
@@ -0,0 +1,248 @@
1
+ import { css, html, LitElement } from 'lit';
2
+ import { base } from '../theme.js';
3
+ /** Delay before a held button starts repeating, then the interval between steps. */
4
+ const REPEAT_DELAY_MS = 400;
5
+ const REPEAT_MS = 60;
6
+ /**
7
+ * A number with a floor. Where the dial is for a value the user feels their way
8
+ * to from a middle, this is for one they already know: a font size, a line
9
+ * width, a corner radius. It reads, it types, and it nudges by one, which is
10
+ * everything a magnitude needs and none of what a wheel is good at. It also
11
+ * costs about a fifth of the width.
12
+ *
13
+ * The value is a real `input[type=number]`, so the keyboard, the spinbutton
14
+ * role and the value semantics are the platform's. Holding a button repeats;
15
+ * holding Shift steps by ten.
16
+ *
17
+ * Emits `slide` while a held button is repeating (tier-A hints) and `commit`
18
+ * when the value settles, so a held button writes one operation and not forty.
19
+ */
20
+ export class SealpixelStepper extends LitElement {
21
+ static { this.styles = [
22
+ base,
23
+ css `
24
+ :host {
25
+ display: block;
26
+ }
27
+ .row {
28
+ display: flex;
29
+ justify-content: center;
30
+ align-items: baseline;
31
+ gap: calc(var(--sealpixel-space) * 0.75);
32
+ margin-bottom: 2px;
33
+ }
34
+ label {
35
+ color: var(--sealpixel-color-fg-muted);
36
+ font-size: 11px;
37
+ letter-spacing: 0.012em;
38
+ }
39
+ .box {
40
+ display: inline-flex;
41
+ align-items: center;
42
+ gap: 2px;
43
+ height: var(--sealpixel-control-height);
44
+ padding: 2px;
45
+ background: var(--sealpixel-color-surface-2);
46
+ border: 1px solid transparent;
47
+ border-radius: var(--sealpixel-radius-pill);
48
+ }
49
+ :host([disabled]) .box {
50
+ opacity: 0.45;
51
+ }
52
+ button {
53
+ width: calc(var(--sealpixel-control-height) - 6px);
54
+ height: calc(var(--sealpixel-control-height) - 6px);
55
+ display: grid;
56
+ place-items: center;
57
+ border: 0;
58
+ border-radius: 50%;
59
+ background: transparent;
60
+ color: var(--sealpixel-color-fg-muted);
61
+ font: inherit;
62
+ font-size: 15px;
63
+ line-height: 1;
64
+ cursor: pointer;
65
+ transition:
66
+ background var(--sealpixel-transition),
67
+ color var(--sealpixel-transition),
68
+ transform var(--sealpixel-duration) var(--sealpixel-ease-spring);
69
+ }
70
+ button:hover:not(:disabled) {
71
+ background: var(--sealpixel-color-surface);
72
+ color: var(--sealpixel-color-fg);
73
+ }
74
+ button:active:not(:disabled) {
75
+ transform: scale(0.9);
76
+ transition-duration: var(--sealpixel-duration-fast);
77
+ }
78
+ button:disabled {
79
+ opacity: 0.4;
80
+ cursor: default;
81
+ }
82
+ /*
83
+ * The field sizes to its digits rather than to the platform's idea of a
84
+ * number box, and the native spinners go: the two buttons are the spinner.
85
+ */
86
+ input {
87
+ width: 3.4em;
88
+ min-width: 0;
89
+ height: 100%;
90
+ padding: 0;
91
+ border: 0;
92
+ background: transparent;
93
+ color: var(--sealpixel-color-fg);
94
+ font: inherit;
95
+ font-size: 13px;
96
+ font-weight: 600;
97
+ font-variant-numeric: tabular-nums;
98
+ text-align: center;
99
+ appearance: textfield;
100
+ }
101
+ input::-webkit-outer-spin-button,
102
+ input::-webkit-inner-spin-button {
103
+ appearance: none;
104
+ margin: 0;
105
+ }
106
+ input:focus {
107
+ outline: none;
108
+ }
109
+ .box:has(input:focus-visible) {
110
+ outline: 2px solid var(--sealpixel-color-focus);
111
+ outline-offset: 2px;
112
+ }
113
+ .unit {
114
+ padding-right: 6px;
115
+ color: var(--sealpixel-color-fg-muted);
116
+ font-size: 11px;
117
+ }
118
+ `,
119
+ ]; }
120
+ static { this.properties = {
121
+ label: { type: String },
122
+ value: { type: Number },
123
+ min: { type: Number },
124
+ max: { type: Number },
125
+ step: { type: Number },
126
+ unit: { type: String },
127
+ decimals: { type: Number },
128
+ disabled: { type: Boolean, reflect: true },
129
+ }; }
130
+ constructor() {
131
+ super();
132
+ this.timer = null;
133
+ this.repeating = false;
134
+ this.hold = (e, dir) => {
135
+ if (this.disabled || (e.button !== 0 && e.pointerType === 'mouse'))
136
+ return;
137
+ e.currentTarget.setPointerCapture?.(e.pointerId);
138
+ this.repeating = false;
139
+ this.bump(dir, e.shiftKey, false);
140
+ this.timer = setTimeout(() => {
141
+ this.repeating = true;
142
+ this.timer = setInterval(() => this.bump(dir, e.shiftKey, true), REPEAT_MS);
143
+ }, REPEAT_DELAY_MS);
144
+ };
145
+ this.release = () => {
146
+ if (this.timer !== null) {
147
+ clearTimeout(this.timer);
148
+ clearInterval(this.timer);
149
+ this.timer = null;
150
+ }
151
+ // A single tap already committed; only a repeat still owes one.
152
+ if (this.repeating) {
153
+ this.repeating = false;
154
+ this.emit('commit');
155
+ }
156
+ };
157
+ this.onChange = (e) => {
158
+ const raw = Number(e.target.value);
159
+ this.value = this.clamp(Number.isFinite(raw) ? raw : this.value);
160
+ // Put the clamped value back, so 9999 in a 0..512 field does not stay there.
161
+ e.target.value = this.value.toFixed(this.decimals);
162
+ this.emit('commit');
163
+ };
164
+ this.onKeydown = (e) => {
165
+ // The field's own arrow handling ignores `decimals` and never fires until
166
+ // blur, so the stepper takes them and commits straight away.
167
+ const dir = e.key === 'ArrowUp' ? 1 : e.key === 'ArrowDown' ? -1 : 0;
168
+ if (dir === 0)
169
+ return;
170
+ e.preventDefault();
171
+ this.bump(dir, e.shiftKey, false);
172
+ };
173
+ this.label = '';
174
+ this.value = 0;
175
+ this.min = 0;
176
+ this.max = 100;
177
+ this.step = 1;
178
+ this.unit = '';
179
+ this.decimals = 0;
180
+ this.disabled = false;
181
+ }
182
+ render() {
183
+ const id = 'stepper';
184
+ return html `
185
+ <div class="row">
186
+ <label for=${id}>${this.label}</label>
187
+ </div>
188
+ <div class="box" part="stepper">
189
+ <button
190
+ type="button"
191
+ part="step-down"
192
+ aria-label=${`${this.label} −`}
193
+ ?disabled=${this.disabled || this.value <= this.min}
194
+ @pointerdown=${(e) => this.hold(e, -1)}
195
+ @pointerup=${this.release}
196
+ @pointercancel=${this.release}
197
+ @pointerleave=${this.release}
198
+ >−</button>
199
+ <input
200
+ part="input"
201
+ id=${id}
202
+ type="number"
203
+ inputmode="decimal"
204
+ .value=${this.value.toFixed(this.decimals)}
205
+ min=${this.min}
206
+ max=${this.max}
207
+ step=${this.step}
208
+ ?disabled=${this.disabled}
209
+ aria-label=${this.label}
210
+ @change=${this.onChange}
211
+ @keydown=${this.onKeydown}
212
+ />
213
+ ${this.unit ? html `<span class="unit" aria-hidden="true">${this.unit}</span>` : ''}
214
+ <button
215
+ type="button"
216
+ part="step-up"
217
+ aria-label=${`${this.label} +`}
218
+ ?disabled=${this.disabled || this.value >= this.max}
219
+ @pointerdown=${(e) => this.hold(e, 1)}
220
+ @pointerup=${this.release}
221
+ @pointercancel=${this.release}
222
+ @pointerleave=${this.release}
223
+ >+</button>
224
+ </div>
225
+ `;
226
+ }
227
+ clamp(v) {
228
+ const factor = 10 ** this.decimals;
229
+ const rounded = Math.round(v * factor) / factor;
230
+ return Math.min(this.max, Math.max(this.min, rounded));
231
+ }
232
+ emit(name) {
233
+ this.dispatchEvent(new CustomEvent(name, { detail: this.value, bubbles: true, composed: true }));
234
+ }
235
+ /** One step. `live` reports progress; the commit follows when the button goes up. */
236
+ bump(dir, shift, live) {
237
+ const next = this.clamp(this.value + dir * this.step * (shift ? 10 : 1));
238
+ if (next === this.value)
239
+ return;
240
+ this.value = next;
241
+ this.emit(live ? 'slide' : 'commit');
242
+ }
243
+ disconnectedCallback() {
244
+ super.disconnectedCallback();
245
+ this.release();
246
+ }
247
+ }
248
+ //# sourceMappingURL=sealpixel-stepper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sealpixel-stepper.js","sourceRoot":"","sources":["../../src/components/sealpixel-stepper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { LitElement } from 'lit';
2
+ import type { UiTool } from '../tool.js';
3
+ /** Tool switcher with roving tabindex. Horizontal on narrow layouts, vertical on wide. */
4
+ export declare class SealpixelToolbar extends LitElement {
5
+ static styles: import("lit").CSSResult[];
6
+ static properties: {
7
+ tools: {
8
+ attribute: boolean;
9
+ };
10
+ active: {
11
+ type: StringConstructor;
12
+ };
13
+ orientation: {
14
+ type: StringConstructor;
15
+ reflect: boolean;
16
+ };
17
+ t: {
18
+ attribute: boolean;
19
+ };
20
+ };
21
+ tools: UiTool[];
22
+ active: string | null;
23
+ orientation: 'horizontal' | 'vertical';
24
+ t: (key: string) => string;
25
+ constructor();
26
+ render(): import("lit").TemplateResult<1>;
27
+ private select;
28
+ private onKeydown;
29
+ }
30
+ //# sourceMappingURL=sealpixel-toolbar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sealpixel-toolbar.d.ts","sourceRoot":"","sources":["../../src/components/sealpixel-toolbar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAE,MAAM,KAAK,CAAC;AAE5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,0FAA0F;AAC1F,qBAAa,gBAAiB,SAAQ,UAAU;IAC9C,OAAgB,MAAM,4BAkGpB;IAEF,OAAgB,UAAU;;;;;;;;;;;;;;MAKxB;IAEM,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,YAAY,GAAG,UAAU,CAAC;IACvC,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;;IAU1B,MAAM;IAkBf,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,SAAS,CAiBf;CACH"}
@@ -0,0 +1,149 @@
1
+ import { css, html, LitElement } from 'lit';
2
+ import { base } from '../theme.js';
3
+ /** Tool switcher with roving tabindex. Horizontal on narrow layouts, vertical on wide. */
4
+ export class SealpixelToolbar extends LitElement {
5
+ static { this.styles = [
6
+ base,
7
+ css `
8
+ :host {
9
+ display: flex;
10
+ padding: 4px;
11
+ background: var(--sealpixel-color-surface);
12
+ border-top: 1px solid var(--sealpixel-color-border);
13
+ overflow: auto;
14
+ scrollbar-width: thin;
15
+ /* Arrow keys move the roving tabindex; the scroll follows it smoothly. */
16
+ scroll-behavior: smooth;
17
+ }
18
+ .tools {
19
+ display: flex;
20
+ gap: 4px;
21
+ /* Centres when it fits, scrolls from the first button when it does not. */
22
+ margin: auto;
23
+ }
24
+ :host([orientation='vertical']) {
25
+ border-top: none;
26
+ border-right: 1px solid var(--sealpixel-color-border);
27
+ }
28
+ :host([orientation='vertical']) .tools {
29
+ flex-direction: column;
30
+ margin: 0;
31
+ }
32
+ button {
33
+ min-width: var(--sealpixel-touch-target);
34
+ min-height: calc(var(--sealpixel-touch-target) + 10px);
35
+ border: 1px solid transparent;
36
+ border-radius: var(--sealpixel-radius);
37
+ background: transparent;
38
+ color: var(--sealpixel-color-fg-muted);
39
+ font: inherit;
40
+ font-size: 11px;
41
+ font-weight: 500;
42
+ letter-spacing: 0.01em;
43
+ cursor: pointer;
44
+ display: flex;
45
+ flex-direction: column;
46
+ align-items: center;
47
+ justify-content: center;
48
+ gap: 4px;
49
+ padding: 6px 4px;
50
+ max-width: 100%;
51
+ transition:
52
+ background var(--sealpixel-transition),
53
+ color var(--sealpixel-transition);
54
+ }
55
+ button:hover {
56
+ color: var(--sealpixel-color-fg);
57
+ background: var(--sealpixel-color-surface-2);
58
+ }
59
+ button[aria-pressed='true'] {
60
+ color: var(--sealpixel-color-accent);
61
+ background: var(--sealpixel-color-accent-soft, var(--sealpixel-color-surface-2));
62
+ }
63
+ .icon {
64
+ font-size: 20px;
65
+ line-height: 1;
66
+ width: 28px;
67
+ height: 28px;
68
+ display: grid;
69
+ place-items: center;
70
+ border-radius: 8px;
71
+ /*
72
+ * The filled pill is the one element that says which tool is open, so
73
+ * it is the one that earns a spring. Colour and shadow ride the
74
+ * standard curve underneath, otherwise the overshoot shows up as a
75
+ * flash of accent at the edges.
76
+ */
77
+ transition:
78
+ transform var(--sealpixel-duration) var(--sealpixel-ease-spring),
79
+ background var(--sealpixel-duration) var(--sealpixel-ease),
80
+ color var(--sealpixel-duration) var(--sealpixel-ease),
81
+ box-shadow var(--sealpixel-duration) var(--sealpixel-ease);
82
+ }
83
+ button:hover .icon {
84
+ transform: translateY(-1px);
85
+ }
86
+ button[aria-pressed='true'] .icon {
87
+ background: var(--sealpixel-color-accent);
88
+ color: var(--sealpixel-color-accent-fg);
89
+ box-shadow: var(--sealpixel-shadow-sm, none);
90
+ transform: none;
91
+ }
92
+ button:active .icon {
93
+ transform: scale(0.92);
94
+ transition-duration: var(--sealpixel-duration-fast);
95
+ }
96
+ .label {
97
+ max-width: 100%;
98
+ overflow: hidden;
99
+ text-overflow: ellipsis;
100
+ white-space: nowrap;
101
+ }
102
+ `,
103
+ ]; }
104
+ static { this.properties = {
105
+ tools: { attribute: false },
106
+ active: { type: String },
107
+ orientation: { type: String, reflect: true },
108
+ t: { attribute: false },
109
+ }; }
110
+ constructor() {
111
+ super();
112
+ this.onKeydown = (e) => {
113
+ const keys = this.orientation === 'vertical' ? ['ArrowUp', 'ArrowDown'] : ['ArrowLeft', 'ArrowRight'];
114
+ const dir = e.key === keys[0] ? -1 : e.key === keys[1] ? 1 : 0;
115
+ if (dir === 0)
116
+ return;
117
+ e.preventDefault();
118
+ const idx = Math.max(0, this.tools.findIndex((t) => t.id === this.active));
119
+ const next = this.tools[(idx + dir + this.tools.length) % this.tools.length];
120
+ if (next) {
121
+ this.select(next.id);
122
+ this.updateComplete.then(() => this.renderRoot.querySelector(`[data-tool="${next.id}"]`)?.focus());
123
+ }
124
+ };
125
+ this.tools = [];
126
+ this.active = null;
127
+ this.orientation = 'horizontal';
128
+ this.t = (k) => k;
129
+ }
130
+ render() {
131
+ return html `<div class="tools" role="toolbar" aria-orientation=${this.orientation} @keydown=${this.onKeydown}>
132
+ ${this.tools.map((tool, i) => html `<button
133
+ part="tool"
134
+ type="button"
135
+ data-tool=${tool.id}
136
+ aria-pressed=${this.active === tool.id ? 'true' : 'false'}
137
+ tabindex=${this.active === tool.id || (this.active === null && i === 0) ? 0 : -1}
138
+ @click=${() => this.select(tool.id)}
139
+ >
140
+ <span class="icon" aria-hidden="true">${tool.icon}</span>
141
+ <span class="label">${this.t(tool.label)}</span>
142
+ </button>`)}
143
+ </div>`;
144
+ }
145
+ select(id) {
146
+ this.dispatchEvent(new CustomEvent('tool-select', { detail: id, bubbles: true, composed: true }));
147
+ }
148
+ }
149
+ //# sourceMappingURL=sealpixel-toolbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sealpixel-toolbar.js","sourceRoot":"","sources":["../../src/components/sealpixel-toolbar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import type { ReactiveController, ReactiveControllerHost } from 'lit';
2
+ export interface GestureHandlers {
3
+ /** One pointer moved. `dx`, `dy` in CSS pixels since the last event. */
4
+ drag?: (e: {
5
+ x: number;
6
+ y: number;
7
+ dx: number;
8
+ dy: number;
9
+ pointerType: string;
10
+ }) => void;
11
+ dragStart?: (e: {
12
+ x: number;
13
+ y: number;
14
+ target: EventTarget | null;
15
+ }) => void;
16
+ dragEnd?: () => void;
17
+ tap?: (e: {
18
+ x: number;
19
+ y: number;
20
+ }) => void;
21
+ /** Two pointers: scale and rotation deltas around the midpoint. */
22
+ pinch?: (e: {
23
+ cx: number;
24
+ cy: number;
25
+ scale: number;
26
+ rotation: number;
27
+ dx: number;
28
+ dy: number;
29
+ }) => void;
30
+ /** Wheel with ctrl/meta, or trackpad pinch. `factor` > 1 zooms in. */
31
+ wheelZoom?: (e: {
32
+ cx: number;
33
+ cy: number;
34
+ factor: number;
35
+ }) => void;
36
+ /** Return `false` to leave the event to the page (it scrolls instead of the stage). */
37
+ wheelPan?: (e: {
38
+ dx: number;
39
+ dy: number;
40
+ }) => boolean | undefined;
41
+ }
42
+ /**
43
+ * Pointer Events only. Recognises drag, tap, two-finger pinch/rotate/pan and
44
+ * wheel gestures. Works the same for mouse, touch and pen.
45
+ */
46
+ export declare class GestureController implements ReactiveController {
47
+ private handlers;
48
+ private pointers;
49
+ private last;
50
+ private moved;
51
+ private pinchPrev;
52
+ private element;
53
+ constructor(host: ReactiveControllerHost, handlers: GestureHandlers);
54
+ attach(element: HTMLElement): void;
55
+ detach(): void;
56
+ hostConnected(): void;
57
+ hostDisconnected(): void;
58
+ private local;
59
+ private onDown;
60
+ private onMove;
61
+ private onUp;
62
+ private onWheel;
63
+ private pinchState;
64
+ }
65
+ //# sourceMappingURL=gesture-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gesture-controller.d.ts","sourceRoot":"","sources":["../../src/controllers/gesture-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,KAAK,CAAC;AAEtE,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC1F,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9E,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC5C,mEAAmE;IACnE,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE;QACV,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;KACZ,KAAK,IAAI,CAAC;IACX,sEAAsE;IACtE,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACpE,uFAAuF;IACvF,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,GAAG,SAAS,CAAC;CACnE;AAOD;;;GAGG;AACH,qBAAa,iBAAkB,YAAW,kBAAkB;IASxD,OAAO,CAAC,QAAQ;IARlB,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,IAAI,CAAkB;IAC9B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,SAAS,CAAwE;IACzF,OAAO,CAAC,OAAO,CAA4B;gBAGzC,IAAI,EAAE,sBAAsB,EACpB,QAAQ,EAAE,eAAe;IAKnC,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAYlC,MAAM,IAAI,IAAI;IAWd,aAAa,IAAI,IAAI;IACrB,gBAAgB,IAAI,IAAI;IAIxB,OAAO,CAAC,KAAK;IAKb,OAAO,CAAC,MAAM,CAYZ;IAEF,OAAO,CAAC,MAAM,CAsBZ;IAEF,OAAO,CAAC,IAAI,CAaV;IAEF,OAAO,CAAC,OAAO,CAQb;IAEF,OAAO,CAAC,UAAU;CASnB"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Pointer Events only. Recognises drag, tap, two-finger pinch/rotate/pan and
3
+ * wheel gestures. Works the same for mouse, touch and pen.
4
+ */
5
+ export class GestureController {
6
+ constructor(host, handlers) {
7
+ this.handlers = handlers;
8
+ this.pointers = new Map();
9
+ this.last = null;
10
+ this.moved = false;
11
+ this.pinchPrev = null;
12
+ this.element = null;
13
+ this.onDown = (e) => {
14
+ if (e.button !== 0 && e.pointerType === 'mouse')
15
+ return;
16
+ this.element.setPointerCapture(e.pointerId);
17
+ const p = this.local(e);
18
+ this.pointers.set(e.pointerId, p);
19
+ if (this.pointers.size === 1) {
20
+ this.last = p;
21
+ this.moved = false;
22
+ this.handlers.dragStart?.({ ...p, target: e.target });
23
+ }
24
+ else if (this.pointers.size === 2) {
25
+ this.pinchPrev = this.pinchState();
26
+ }
27
+ };
28
+ this.onMove = (e) => {
29
+ if (!this.pointers.has(e.pointerId))
30
+ return;
31
+ const p = this.local(e);
32
+ this.pointers.set(e.pointerId, p);
33
+ if (this.pointers.size === 1 && this.last) {
34
+ const dx = p.x - this.last.x;
35
+ const dy = p.y - this.last.y;
36
+ if (Math.abs(dx) + Math.abs(dy) > 0)
37
+ this.moved = true;
38
+ this.last = p;
39
+ this.handlers.drag?.({ ...p, dx, dy, pointerType: e.pointerType });
40
+ }
41
+ else if (this.pointers.size === 2 && this.pinchPrev) {
42
+ const now = this.pinchState();
43
+ this.handlers.pinch?.({
44
+ cx: now.cx,
45
+ cy: now.cy,
46
+ scale: this.pinchPrev.dist === 0 ? 1 : now.dist / this.pinchPrev.dist,
47
+ rotation: now.angle - this.pinchPrev.angle,
48
+ dx: now.cx - this.pinchPrev.cx,
49
+ dy: now.cy - this.pinchPrev.cy,
50
+ });
51
+ this.pinchPrev = now;
52
+ }
53
+ };
54
+ this.onUp = (e) => {
55
+ if (!this.pointers.has(e.pointerId))
56
+ return;
57
+ const wasSingle = this.pointers.size === 1;
58
+ this.pointers.delete(e.pointerId);
59
+ if (wasSingle) {
60
+ if (!this.moved)
61
+ this.handlers.tap?.(this.local(e));
62
+ this.handlers.dragEnd?.();
63
+ this.last = null;
64
+ }
65
+ if (this.pointers.size < 2)
66
+ this.pinchPrev = null;
67
+ if (this.pointers.size === 1) {
68
+ this.last = [...this.pointers.values()][0] ?? null;
69
+ }
70
+ };
71
+ this.onWheel = (e) => {
72
+ const p = this.local(e);
73
+ if (e.ctrlKey || e.metaKey) {
74
+ e.preventDefault();
75
+ this.handlers.wheelZoom?.({ cx: p.x, cy: p.y, factor: Math.exp(-e.deltaY * 0.01) });
76
+ }
77
+ else if (this.handlers.wheelPan) {
78
+ if (this.handlers.wheelPan({ dx: -e.deltaX, dy: -e.deltaY }) !== false)
79
+ e.preventDefault();
80
+ }
81
+ };
82
+ host.addController(this);
83
+ }
84
+ attach(element) {
85
+ if (this.element === element)
86
+ return;
87
+ this.detach();
88
+ this.element = element;
89
+ element.style.touchAction = 'none';
90
+ element.addEventListener('pointerdown', this.onDown);
91
+ element.addEventListener('pointermove', this.onMove);
92
+ element.addEventListener('pointerup', this.onUp);
93
+ element.addEventListener('pointercancel', this.onUp);
94
+ element.addEventListener('wheel', this.onWheel, { passive: false });
95
+ }
96
+ detach() {
97
+ const el = this.element;
98
+ if (!el)
99
+ return;
100
+ el.removeEventListener('pointerdown', this.onDown);
101
+ el.removeEventListener('pointermove', this.onMove);
102
+ el.removeEventListener('pointerup', this.onUp);
103
+ el.removeEventListener('pointercancel', this.onUp);
104
+ el.removeEventListener('wheel', this.onWheel);
105
+ this.element = null;
106
+ }
107
+ hostConnected() { }
108
+ hostDisconnected() {
109
+ this.detach();
110
+ }
111
+ local(e) {
112
+ const r = this.element.getBoundingClientRect();
113
+ return { x: e.clientX - r.left, y: e.clientY - r.top };
114
+ }
115
+ pinchState() {
116
+ const [a, b] = [...this.pointers.values()];
117
+ return {
118
+ dist: Math.hypot(b.x - a.x, b.y - a.y),
119
+ angle: Math.atan2(b.y - a.y, b.x - a.x),
120
+ cx: (a.x + b.x) / 2,
121
+ cy: (a.y + b.y) / 2,
122
+ };
123
+ }
124
+ }
125
+ //# sourceMappingURL=gesture-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gesture-controller.js","sourceRoot":"","sources":["../../src/controllers/gesture-controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { EditorSession, SessionState } from '@sealpixel/core';
2
+ import type { ReactiveController, ReactiveControllerHost } from 'lit';
3
+ /** Re-renders the host whenever the session state changes. */
4
+ export declare class SessionController implements ReactiveController {
5
+ private readonly host;
6
+ private _session;
7
+ private unsubscribe;
8
+ state: SessionState | null;
9
+ constructor(host: ReactiveControllerHost, _session: EditorSession | null);
10
+ get session(): EditorSession | null;
11
+ set session(next: EditorSession | null);
12
+ hostConnected(): void;
13
+ hostDisconnected(): void;
14
+ }
15
+ //# sourceMappingURL=session-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-controller.d.ts","sourceRoot":"","sources":["../../src/controllers/session-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,KAAK,CAAC;AAEtE,8DAA8D;AAC9D,qBAAa,iBAAkB,YAAW,kBAAkB;IAKxD,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ;IALlB,OAAO,CAAC,WAAW,CAA6B;IAChD,KAAK,EAAE,YAAY,GAAG,IAAI,CAAQ;gBAGf,IAAI,EAAE,sBAAsB,EACrC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAMxC,IAAI,OAAO,IAAI,aAAa,GAAG,IAAI,CAElC;IAED,IAAI,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,EAQrC;IAED,aAAa,IAAI,IAAI;IAQrB,gBAAgB,IAAI,IAAI;CAIzB"}