@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,344 @@
1
+ import { css, html, LitElement, svg } from 'lit';
2
+ import { GestureController } from '../controllers/gesture-controller.js';
3
+ import { base } from '../theme.js';
4
+ import { apply, invert, previewToSource, sameAffine, scaleOf, } from '../view-mapping.js';
5
+ const DOUBLE_TAP_MS = 350;
6
+ const DOUBLE_TAP_PX = 24;
7
+ /**
8
+ * Shows the preview on a canvas, fitted and zoomable, with an SVG overlay in
9
+ * image pixel coordinates for tool gizmos. Tier-A hints (CSS filter and
10
+ * transform) are applied to the canvas until the next preview arrives.
11
+ */
12
+ export class LumbraStage extends LitElement {
13
+ static { this.styles = [
14
+ base,
15
+ css `
16
+ :host {
17
+ display: block;
18
+ position: relative;
19
+ overflow: hidden;
20
+ background-color: var(--lumbra-color-bg);
21
+ background-image:
22
+ linear-gradient(45deg, var(--lumbra-stage-checker-a) 25%, transparent 25%),
23
+ linear-gradient(-45deg, var(--lumbra-stage-checker-a) 25%, transparent 25%),
24
+ linear-gradient(45deg, transparent 75%, var(--lumbra-stage-checker-a) 75%),
25
+ linear-gradient(-45deg, transparent 75%, var(--lumbra-stage-checker-a) 75%);
26
+ background-size: 16px 16px;
27
+ background-position: 0 0, 0 8px, 8px -8px, -8px 0;
28
+ touch-action: none;
29
+ user-select: none;
30
+ }
31
+ .viewport {
32
+ position: absolute;
33
+ inset: 0;
34
+ }
35
+ .content {
36
+ position: absolute;
37
+ left: 0;
38
+ top: 0;
39
+ transform-origin: 0 0;
40
+ will-change: transform;
41
+ }
42
+ canvas {
43
+ display: block;
44
+ image-rendering: auto;
45
+ transform-origin: 50% 50%;
46
+ box-shadow: var(--lumbra-shadow, none);
47
+ }
48
+ svg.overlay {
49
+ position: absolute;
50
+ left: 0;
51
+ top: 0;
52
+ overflow: visible;
53
+ pointer-events: none;
54
+ }
55
+ svg.overlay [data-interactive] {
56
+ pointer-events: all;
57
+ }
58
+ .status {
59
+ position: absolute;
60
+ right: var(--lumbra-space);
61
+ bottom: var(--lumbra-space);
62
+ padding: 3px 9px;
63
+ border: 0;
64
+ border-radius: 999px;
65
+ background: color-mix(in srgb, var(--lumbra-color-surface) 85%, transparent);
66
+ color: var(--lumbra-color-fg-muted);
67
+ font: inherit;
68
+ font-size: 12px;
69
+ font-variant-numeric: tabular-nums;
70
+ backdrop-filter: blur(6px);
71
+ cursor: pointer;
72
+ }
73
+ .status:disabled {
74
+ cursor: default;
75
+ }
76
+ .status:not(:disabled):hover {
77
+ color: var(--lumbra-color-fg);
78
+ }
79
+ .status:focus-visible {
80
+ outline: 2px solid var(--lumbra-color-focus);
81
+ outline-offset: 2px;
82
+ }
83
+ .pending {
84
+ position: absolute;
85
+ left: var(--lumbra-space);
86
+ top: var(--lumbra-space);
87
+ width: 8px;
88
+ height: 8px;
89
+ border-radius: 50%;
90
+ background: var(--lumbra-color-accent);
91
+ opacity: 0.8;
92
+ }
93
+ `,
94
+ ]; }
95
+ static { this.properties = {
96
+ preview: { attribute: false },
97
+ pending: { type: Boolean },
98
+ overlay: { attribute: false },
99
+ hintFilter: { attribute: false },
100
+ hintTransform: { attribute: false },
101
+ zoom: { type: Number },
102
+ panEnabled: { type: Boolean },
103
+ t: { attribute: false },
104
+ }; }
105
+ constructor() {
106
+ super();
107
+ this.canvas = null;
108
+ this.drawnVersion = -1;
109
+ this.pan = { x: 0, y: 0 };
110
+ this.fitted = false;
111
+ /** Preview px → source px of the drawn preview; a change means the stage shows another view. */
112
+ this.viewMap = null;
113
+ this.lastTap = null;
114
+ this.resizeObserver = null;
115
+ this.gestures = new GestureController(this, {
116
+ drag: (e) => {
117
+ if (!this.panEnabled)
118
+ return;
119
+ this.panBy(e.dx, e.dy);
120
+ },
121
+ // A second tap close to the first fits the image again (double-tap, double-click).
122
+ tap: (e) => {
123
+ const now = performance.now();
124
+ const prev = this.lastTap;
125
+ if (prev &&
126
+ now - prev.at < DOUBLE_TAP_MS &&
127
+ Math.hypot(e.x - prev.x, e.y - prev.y) < DOUBLE_TAP_PX) {
128
+ this.lastTap = null;
129
+ this.fit();
130
+ return;
131
+ }
132
+ this.lastTap = { x: e.x, y: e.y, at: now };
133
+ },
134
+ pinch: (e) => this.zoomAround(e.scale, e.cx, e.cy, e.dx, e.dy),
135
+ wheelZoom: (e) => this.zoomAround(e.factor, e.cx, e.cy, 0, 0),
136
+ // Plain scrolling pans only while the image is larger than the view;
137
+ // otherwise the page scrolls as the user expects.
138
+ wheelPan: (e) => {
139
+ if (!this.overflows())
140
+ return false;
141
+ this.panBy(e.dx, e.dy);
142
+ return true;
143
+ },
144
+ });
145
+ this.preview = null;
146
+ this.pending = false;
147
+ this.overlay = null;
148
+ this.hintFilter = '';
149
+ this.hintTransform = '';
150
+ this.zoom = 1;
151
+ this.panEnabled = true;
152
+ this.t = (key) => key;
153
+ }
154
+ get geometry() {
155
+ return this.preview?.geometry ?? null;
156
+ }
157
+ get imageWidth() {
158
+ return this.preview?.image.width ?? 0;
159
+ }
160
+ get imageHeight() {
161
+ return this.preview?.image.height ?? 0;
162
+ }
163
+ /** Converts a client point into preview image pixels. */
164
+ toImage(clientX, clientY) {
165
+ const r = this.getBoundingClientRect();
166
+ return {
167
+ x: (clientX - r.left - this.pan.x) / this.zoom,
168
+ y: (clientY - r.top - this.pan.y) / this.zoom,
169
+ };
170
+ }
171
+ fit() {
172
+ const w = this.imageWidth;
173
+ const h = this.imageHeight;
174
+ if (!w || !h)
175
+ return;
176
+ const r = this.getBoundingClientRect();
177
+ this.zoom = this.fitZoom();
178
+ this.pan = { x: (r.width - w * this.zoom) / 2, y: (r.height - h * this.zoom) / 2 };
179
+ this.fitted = true;
180
+ this.requestUpdate();
181
+ this.viewChanged();
182
+ }
183
+ fitZoom() {
184
+ const r = this.getBoundingClientRect();
185
+ const margin = 24;
186
+ const zoom = Math.min((r.width - margin) / this.imageWidth, (r.height - margin) / this.imageHeight, 4);
187
+ return Math.max(zoom, 0.01);
188
+ }
189
+ /**
190
+ * The preview now shows another view of the same source (a tool switched
191
+ * between cropped and uncropped, a resize changed the output). Keep the
192
+ * source pixel under the view centre where it was and keep its size on
193
+ * screen, instead of fitting and letting the image jump. A view that was
194
+ * fitted is fitted again only when the steady view would overflow.
195
+ */
196
+ reanchor(prev, next) {
197
+ const r = this.getBoundingClientRect();
198
+ const cx = r.width / 2;
199
+ const cy = r.height / 2;
200
+ const centre = apply(prev, (cx - this.pan.x) / this.zoom, (cy - this.pan.y) / this.zoom);
201
+ const q = apply(invert(next), centre.x, centre.y);
202
+ const zoom = Math.min(16, Math.max(0.05, (this.zoom * scaleOf(prev)) / scaleOf(next)));
203
+ const iw = this.imageWidth * zoom;
204
+ const ih = this.imageHeight * zoom;
205
+ if (this.fitted && (iw > r.width || ih > r.height)) {
206
+ this.fit();
207
+ return;
208
+ }
209
+ this.zoom = zoom;
210
+ this.pan = this.clampPan({ x: cx - q.x * zoom, y: cy - q.y * zoom });
211
+ this.fitted = Math.abs(zoom - this.fitZoom()) < 1e-3;
212
+ this.requestUpdate();
213
+ this.viewChanged();
214
+ }
215
+ /** True when the zoomed image is larger than the view in at least one axis. */
216
+ overflows() {
217
+ const r = this.getBoundingClientRect();
218
+ return this.imageWidth * this.zoom > r.width || this.imageHeight * this.zoom > r.height;
219
+ }
220
+ /**
221
+ * Keeps the image in view: an image smaller than the view stays fully
222
+ * visible, a larger one never shows empty space beyond its edges.
223
+ */
224
+ clampPan(pan) {
225
+ const r = this.getBoundingClientRect();
226
+ const iw = this.imageWidth * this.zoom;
227
+ const ih = this.imageHeight * this.zoom;
228
+ if (!iw || !ih)
229
+ return pan;
230
+ const axis = (v, size, view) => size <= view ? clamp(v, 0, view - size) : clamp(v, view - size, 0);
231
+ return { x: axis(pan.x, iw, r.width), y: axis(pan.y, ih, r.height) };
232
+ }
233
+ panBy(dx, dy) {
234
+ const next = this.clampPan({ x: this.pan.x + dx, y: this.pan.y + dy });
235
+ if (next.x === this.pan.x && next.y === this.pan.y)
236
+ return;
237
+ this.pan = next;
238
+ this.requestUpdate();
239
+ this.viewChanged();
240
+ }
241
+ /** Zoom or pan changed: overlays scale their gizmos by zoom and must re-render. */
242
+ viewChanged() {
243
+ this.dispatchEvent(new CustomEvent('stage-view', { detail: { zoom: this.zoom, pan: this.pan } }));
244
+ }
245
+ connectedCallback() {
246
+ super.connectedCallback();
247
+ this.resizeObserver = new ResizeObserver(() => {
248
+ if (this.fitted)
249
+ this.fit();
250
+ });
251
+ this.resizeObserver.observe(this);
252
+ }
253
+ disconnectedCallback() {
254
+ super.disconnectedCallback();
255
+ this.resizeObserver?.disconnect();
256
+ }
257
+ firstUpdated() {
258
+ this.canvas = this.renderRoot.querySelector('canvas');
259
+ this.gestures.attach(this.renderRoot.querySelector('.viewport'));
260
+ }
261
+ updated(changed) {
262
+ if (changed.has('preview'))
263
+ this.draw();
264
+ }
265
+ draw() {
266
+ const canvas = this.canvas;
267
+ const preview = this.preview;
268
+ if (!canvas || !preview)
269
+ return;
270
+ const { image } = preview;
271
+ const sizeChanged = canvas.width !== image.width || canvas.height !== image.height;
272
+ if (sizeChanged) {
273
+ canvas.width = image.width;
274
+ canvas.height = image.height;
275
+ }
276
+ if (preview.version !== this.drawnVersion || sizeChanged) {
277
+ const ctx = canvas.getContext('2d');
278
+ if (ctx) {
279
+ const data = new Uint8ClampedArray(image.data.byteLength);
280
+ data.set(image.data);
281
+ ctx.putImageData(new ImageData(data, image.width, image.height), 0, 0);
282
+ }
283
+ this.drawnVersion = preview.version;
284
+ // A real preview landed: drop tier-A hints.
285
+ this.hintFilter = '';
286
+ this.hintTransform = '';
287
+ }
288
+ const nextMap = previewToSource(preview.viewGeometry);
289
+ const prevMap = this.viewMap;
290
+ this.viewMap = nextMap;
291
+ if (prevMap && !sameAffine(prevMap, nextMap))
292
+ this.reanchor(prevMap, nextMap);
293
+ else if (!prevMap || (!this.fitted && sizeChanged))
294
+ this.fit();
295
+ }
296
+ zoomAround(factor, cx, cy, dx, dy) {
297
+ const next = Math.min(16, Math.max(0.05, this.zoom * factor));
298
+ const k = next / this.zoom;
299
+ this.zoom = next;
300
+ this.pan = this.clampPan({
301
+ x: cx - (cx - this.pan.x) * k + dx,
302
+ y: cy - (cy - this.pan.y) * k + dy,
303
+ });
304
+ this.fitted = false;
305
+ this.requestUpdate();
306
+ this.viewChanged();
307
+ }
308
+ render() {
309
+ const w = this.imageWidth;
310
+ const h = this.imageHeight;
311
+ const transform = `translate(${this.pan.x}px, ${this.pan.y}px) scale(${this.zoom})`;
312
+ return html `
313
+ <div class="viewport" part="viewport">
314
+ <div class="content" style="transform:${transform}">
315
+ <canvas
316
+ part="canvas"
317
+ width=${w}
318
+ height=${h}
319
+ style="filter:${this.hintFilter || 'none'};transform:${this.hintTransform || 'none'}"
320
+ role="img"
321
+ aria-label="preview"
322
+ ></canvas>
323
+ ${this.overlay
324
+ ? svg `<svg class="overlay" part="overlay" width=${w} height=${h} viewBox="0 0 ${w} ${h}">${this.overlay}</svg>`
325
+ : null}
326
+ </div>
327
+ </div>
328
+ ${this.pending ? html `<div class="pending" aria-hidden="true"></div>` : null}
329
+ <button
330
+ type="button"
331
+ class="status"
332
+ part="status"
333
+ ?disabled=${this.fitted}
334
+ title=${this.t('stage.fit')}
335
+ aria-label=${`${this.t('stage.zoom', { percent: Math.round(this.zoom * 100) })}. ${this.t('stage.fit')}`}
336
+ @click=${() => this.fit()}
337
+ >${Math.round(this.zoom * 100)}%</button>
338
+ `;
339
+ }
340
+ }
341
+ function clamp(v, lo, hi) {
342
+ return v < lo ? lo : v > hi ? hi : v;
343
+ }
344
+ //# sourceMappingURL=lumbra-stage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lumbra-stage.js","sourceRoot":"","sources":["../../src/components/lumbra-stage.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 LumbraToolbar 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=lumbra-toolbar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lumbra-toolbar.d.ts","sourceRoot":"","sources":["../../src/components/lumbra-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,aAAc,SAAQ,UAAU;IAC3C,OAAgB,MAAM,4BA4EpB;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,127 @@
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 LumbraToolbar extends LitElement {
5
+ static { this.styles = [
6
+ base,
7
+ css `
8
+ :host {
9
+ display: flex;
10
+ padding: 4px;
11
+ background: var(--lumbra-color-surface);
12
+ border-top: 1px solid var(--lumbra-color-border);
13
+ overflow: auto;
14
+ scrollbar-width: thin;
15
+ }
16
+ .tools {
17
+ display: flex;
18
+ gap: 4px;
19
+ /* Centres when it fits, scrolls from the first button when it does not. */
20
+ margin: auto;
21
+ }
22
+ :host([orientation='vertical']) {
23
+ border-top: none;
24
+ border-right: 1px solid var(--lumbra-color-border);
25
+ }
26
+ :host([orientation='vertical']) .tools {
27
+ flex-direction: column;
28
+ margin: 0;
29
+ }
30
+ button {
31
+ min-width: var(--lumbra-touch-target);
32
+ min-height: calc(var(--lumbra-touch-target) + 10px);
33
+ border: 1px solid transparent;
34
+ border-radius: var(--lumbra-radius);
35
+ background: transparent;
36
+ color: var(--lumbra-color-fg-muted);
37
+ font: inherit;
38
+ font-size: 11px;
39
+ font-weight: 500;
40
+ letter-spacing: 0.01em;
41
+ cursor: pointer;
42
+ display: flex;
43
+ flex-direction: column;
44
+ align-items: center;
45
+ justify-content: center;
46
+ gap: 4px;
47
+ padding: 6px 4px;
48
+ max-width: 100%;
49
+ transition:
50
+ background var(--lumbra-transition),
51
+ color var(--lumbra-transition);
52
+ }
53
+ button:hover {
54
+ color: var(--lumbra-color-fg);
55
+ background: var(--lumbra-color-surface-2);
56
+ }
57
+ button[aria-pressed='true'] {
58
+ color: var(--lumbra-color-accent);
59
+ background: var(--lumbra-color-accent-soft, var(--lumbra-color-surface-2));
60
+ }
61
+ .icon {
62
+ font-size: 20px;
63
+ line-height: 1;
64
+ width: 28px;
65
+ height: 28px;
66
+ display: grid;
67
+ place-items: center;
68
+ border-radius: 8px;
69
+ }
70
+ button[aria-pressed='true'] .icon {
71
+ background: var(--lumbra-color-accent);
72
+ color: var(--lumbra-color-accent-fg);
73
+ }
74
+ .label {
75
+ max-width: 100%;
76
+ overflow: hidden;
77
+ text-overflow: ellipsis;
78
+ white-space: nowrap;
79
+ }
80
+ `,
81
+ ]; }
82
+ static { this.properties = {
83
+ tools: { attribute: false },
84
+ active: { type: String },
85
+ orientation: { type: String, reflect: true },
86
+ t: { attribute: false },
87
+ }; }
88
+ constructor() {
89
+ super();
90
+ this.onKeydown = (e) => {
91
+ const keys = this.orientation === 'vertical' ? ['ArrowUp', 'ArrowDown'] : ['ArrowLeft', 'ArrowRight'];
92
+ const dir = e.key === keys[0] ? -1 : e.key === keys[1] ? 1 : 0;
93
+ if (dir === 0)
94
+ return;
95
+ e.preventDefault();
96
+ const idx = Math.max(0, this.tools.findIndex((t) => t.id === this.active));
97
+ const next = this.tools[(idx + dir + this.tools.length) % this.tools.length];
98
+ if (next) {
99
+ this.select(next.id);
100
+ this.updateComplete.then(() => this.renderRoot.querySelector(`[data-tool="${next.id}"]`)?.focus());
101
+ }
102
+ };
103
+ this.tools = [];
104
+ this.active = null;
105
+ this.orientation = 'horizontal';
106
+ this.t = (k) => k;
107
+ }
108
+ render() {
109
+ return html `<div class="tools" role="toolbar" aria-orientation=${this.orientation} @keydown=${this.onKeydown}>
110
+ ${this.tools.map((tool, i) => html `<button
111
+ part="tool"
112
+ type="button"
113
+ data-tool=${tool.id}
114
+ aria-pressed=${this.active === tool.id ? 'true' : 'false'}
115
+ tabindex=${this.active === tool.id || (this.active === null && i === 0) ? 0 : -1}
116
+ @click=${() => this.select(tool.id)}
117
+ >
118
+ <span class="icon" aria-hidden="true">${tool.icon}</span>
119
+ <span class="label">${this.t(tool.label)}</span>
120
+ </button>`)}
121
+ </div>`;
122
+ }
123
+ select(id) {
124
+ this.dispatchEvent(new CustomEvent('tool-select', { detail: id, bubbles: true, composed: true }));
125
+ }
126
+ }
127
+ //# sourceMappingURL=lumbra-toolbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lumbra-toolbar.js","sourceRoot":"","sources":["../../src/components/lumbra-toolbar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { LitElement } from 'lit';
2
+ export declare class PincelButton extends LitElement {
3
+ static styles: import("lit").CSSResult[];
4
+ static properties: {
5
+ disabled: {
6
+ type: BooleanConstructor;
7
+ reflect: boolean;
8
+ };
9
+ pressed: {
10
+ type: BooleanConstructor;
11
+ reflect: boolean;
12
+ };
13
+ variant: {
14
+ type: StringConstructor;
15
+ reflect: boolean;
16
+ };
17
+ label: {
18
+ type: StringConstructor;
19
+ };
20
+ };
21
+ disabled: boolean;
22
+ pressed: boolean;
23
+ variant: 'default' | 'primary' | 'ghost';
24
+ label: string;
25
+ constructor();
26
+ render(): import("lit").TemplateResult<1>;
27
+ }
28
+ //# sourceMappingURL=pincel-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pincel-button.d.ts","sourceRoot":"","sources":["../../src/components/pincel-button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAE,MAAM,KAAK,CAAC;AAG5C,qBAAa,YAAa,SAAQ,UAAU;IAC1C,OAAgB,MAAM,4BAwDpB;IAEF,OAAgB,UAAU;;;;;;;;;;;;;;;;MAKxB;IAEM,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IACzC,KAAK,EAAE,MAAM,CAAC;;IAUb,MAAM;CAYhB"}
@@ -0,0 +1,87 @@
1
+ import { css, html, LitElement } from 'lit';
2
+ import { base } from '../theme.js';
3
+ export class PincelButton extends LitElement {
4
+ static { this.styles = [
5
+ base,
6
+ css `
7
+ :host {
8
+ display: inline-block;
9
+ }
10
+ button {
11
+ min-width: var(--pincel-touch-target);
12
+ min-height: var(--pincel-touch-target);
13
+ padding: 0 calc(var(--pincel-space) * 1.5);
14
+ border: 1px solid var(--pincel-color-border);
15
+ border-radius: var(--pincel-radius-sm);
16
+ background: var(--pincel-color-surface-2);
17
+ color: var(--pincel-color-fg);
18
+ font: inherit;
19
+ font-weight: 500;
20
+ cursor: pointer;
21
+ display: inline-flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ gap: var(--pincel-space);
25
+ width: 100%;
26
+ transition:
27
+ background var(--pincel-transition),
28
+ border-color var(--pincel-transition),
29
+ transform var(--pincel-transition);
30
+ }
31
+ button:hover:not(:disabled) {
32
+ background: var(--pincel-color-surface-3, var(--pincel-color-surface-2));
33
+ border-color: var(--pincel-color-fg-muted);
34
+ }
35
+ button:active:not(:disabled) {
36
+ transform: translateY(1px);
37
+ }
38
+ button:disabled {
39
+ opacity: 0.45;
40
+ cursor: default;
41
+ }
42
+ :host([variant='primary']) button {
43
+ background: var(--pincel-color-accent);
44
+ border-color: var(--pincel-color-accent);
45
+ color: var(--pincel-color-accent-fg);
46
+ }
47
+ :host([variant='ghost']) button {
48
+ background: transparent;
49
+ border-color: transparent;
50
+ }
51
+ :host([pressed]) button {
52
+ background: var(--pincel-color-accent-soft, transparent);
53
+ border-color: var(--pincel-color-accent);
54
+ color: var(--pincel-color-fg);
55
+ }
56
+ :host([variant='primary']) button:hover:not(:disabled) {
57
+ filter: brightness(1.08);
58
+ }
59
+ `,
60
+ ]; }
61
+ static { this.properties = {
62
+ disabled: { type: Boolean, reflect: true },
63
+ pressed: { type: Boolean, reflect: true },
64
+ variant: { type: String, reflect: true },
65
+ label: { type: String },
66
+ }; }
67
+ constructor() {
68
+ super();
69
+ this.disabled = false;
70
+ this.pressed = false;
71
+ this.variant = 'default';
72
+ this.label = '';
73
+ }
74
+ render() {
75
+ return html `<button
76
+ part="button"
77
+ type="button"
78
+ ?disabled=${this.disabled}
79
+ aria-pressed=${this.pressed ? 'true' : 'false'}
80
+ aria-label=${this.label || undefined}
81
+ title=${this.label || undefined}
82
+ >
83
+ <slot></slot>
84
+ </button>`;
85
+ }
86
+ }
87
+ //# sourceMappingURL=pincel-button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pincel-button.js","sourceRoot":"","sources":["../../src/components/pincel-button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC,MAAM,OAAO,YAAa,SAAQ,UAAU;aAC1B,WAAM,GAAG;QACvB,IAAI;QACJ,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqDF;KACF,CAAC;aAEc,eAAU,GAAG;QAC3B,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;QAC1C,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;QACzC,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QACxC,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;KACxB,CAAC;IAOF;QACE,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;IAClB,CAAC;IAEQ,MAAM;QACb,OAAO,IAAI,CAAA;;;kBAGG,IAAI,CAAC,QAAQ;qBACV,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;mBACjC,IAAI,CAAC,KAAK,IAAI,SAAS;cAC5B,IAAI,CAAC,KAAK,IAAI,SAAS;;;cAGvB,CAAC;IACb,CAAC"}