@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,118 @@
1
+ import type { Geometry, PreviewState } from '@sealpixel/core';
2
+ import { LitElement, type TemplateResult } from 'lit';
3
+ type Translate = (key: string, params?: Record<string, string | number>) => string;
4
+ /**
5
+ * Shows the preview on a canvas, fitted and zoomable, with an SVG overlay in
6
+ * image pixel coordinates for tool gizmos. Tier-A hints (CSS filter and
7
+ * transform) are applied to the canvas until the next preview arrives.
8
+ */
9
+ export declare class SealpixelStage extends LitElement {
10
+ static styles: import("lit").CSSResult[];
11
+ static properties: {
12
+ preview: {
13
+ attribute: boolean;
14
+ };
15
+ pending: {
16
+ type: BooleanConstructor;
17
+ };
18
+ overlay: {
19
+ attribute: boolean;
20
+ };
21
+ hintFilter: {
22
+ attribute: boolean;
23
+ };
24
+ hintTransform: {
25
+ attribute: boolean;
26
+ };
27
+ clip: {
28
+ attribute: boolean;
29
+ };
30
+ zoom: {
31
+ type: NumberConstructor;
32
+ };
33
+ panEnabled: {
34
+ type: BooleanConstructor;
35
+ };
36
+ t: {
37
+ attribute: boolean;
38
+ };
39
+ };
40
+ preview: PreviewState | null;
41
+ pending: boolean;
42
+ overlay: TemplateResult | null;
43
+ hintFilter: string;
44
+ hintTransform: string;
45
+ /**
46
+ * A CSS shape the preview is clipped to, or an empty string. A tool uses it
47
+ * to take pixels away rather than paint over them: crop clips to its
48
+ * rectangle so the stage's own backdrop shows around the result, which is
49
+ * the only way the checker carries through without seams. Unlike a hint it
50
+ * survives the next preview, because the tool owns it.
51
+ */
52
+ clip: string;
53
+ zoom: number;
54
+ panEnabled: boolean;
55
+ t: Translate;
56
+ private canvas;
57
+ private drawnVersion;
58
+ private pan;
59
+ private fitted;
60
+ /** Preview px → source px of the drawn preview; a change means the stage shows another view. */
61
+ private viewMap;
62
+ private lastTap;
63
+ /**
64
+ * True while the next transform should be animated. Every path that moves the
65
+ * view sets it, so it can never linger from an earlier action into a drag.
66
+ */
67
+ private glide;
68
+ /**
69
+ * Set by a tool that wants the next preview to arrive as a movement rather
70
+ * than a cut. Crop uses it: leaving the tool is the moment the image really
71
+ * becomes the crop, and seeing it close is the only signal the user gets
72
+ * that anything happened.
73
+ */
74
+ private glidePending;
75
+ private resizeObserver;
76
+ private readonly gestures;
77
+ constructor();
78
+ get geometry(): Geometry | null;
79
+ get imageWidth(): number;
80
+ get imageHeight(): number;
81
+ /** Converts a client point into preview image pixels. */
82
+ toImage(clientX: number, clientY: number): {
83
+ x: number;
84
+ y: number;
85
+ };
86
+ /** Asks for the next preview-driven view change to be animated, once. */
87
+ glideNextView(): void;
88
+ /** `glide` animates the move; a resize or a first load must not animate. */
89
+ fit(glide?: boolean): void;
90
+ private fitZoom;
91
+ /**
92
+ * The preview now shows another view of the same source (a tool switched
93
+ * between cropped and uncropped, a resize changed the output). Keep the
94
+ * source pixel under the view centre where it was and keep its size on
95
+ * screen, instead of fitting and letting the image jump. A view that was
96
+ * fitted is fitted again only when the steady view would overflow.
97
+ */
98
+ private reanchor;
99
+ /** True when the zoomed image is larger than the view in at least one axis. */
100
+ private overflows;
101
+ /**
102
+ * Keeps the image in view: an image smaller than the view stays fully
103
+ * visible, a larger one never shows empty space beyond its edges.
104
+ */
105
+ private clampPan;
106
+ private panBy;
107
+ /** Zoom or pan changed: overlays scale their gizmos by zoom and must re-render. */
108
+ private viewChanged;
109
+ connectedCallback(): void;
110
+ disconnectedCallback(): void;
111
+ firstUpdated(): void;
112
+ updated(changed: Map<string, unknown>): void;
113
+ private draw;
114
+ private zoomAround;
115
+ render(): TemplateResult<1>;
116
+ }
117
+ export {};
118
+ //# sourceMappingURL=sealpixel-stage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sealpixel-stage.d.ts","sourceRoot":"","sources":["../../src/components/sealpixel-stage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAa,UAAU,EAAO,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAYtE,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,MAAM,CAAC;AAInF;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,UAAU;IAC5C,OAAgB,MAAM,4BA+GpB;IAEF,OAAgB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAUxB;IAEM,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IAC9B;;;;;;OAMG;IACK,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,CAAC,EAAE,SAAS,CAAC;IAErB,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,gGAAgG;IAChG,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,OAAO,CAAqD;IACpE;;;OAGG;IACH,OAAO,CAAC,KAAK,CAAS;IACtB;;;;;OAKG;IACH,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CA6BtB;;IAeH,IAAI,QAAQ,IAAI,QAAQ,GAAG,IAAI,CAE9B;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,yDAAyD;IACzD,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;IAQnE,yEAAyE;IACzE,aAAa,IAAI,IAAI;IAIrB,4EAA4E;IAC5E,GAAG,CAAC,KAAK,UAAQ,GAAG,IAAI;IAaxB,OAAO,CAAC,OAAO;IAWf;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ;IAwBhB,+EAA+E;IAC/E,OAAO,CAAC,SAAS;IAKjB;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,KAAK;IAUb,mFAAmF;IACnF,OAAO,CAAC,WAAW;IAMV,iBAAiB,IAAI,IAAI;IAQzB,oBAAoB,IAAI,IAAI;IAK5B,YAAY,IAAI,IAAI;IAKpB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIrD,OAAO,CAAC,IAAI;IAmCZ,OAAO,CAAC,UAAU;IAeT,MAAM;CAkChB"}
@@ -0,0 +1,409 @@
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 SealpixelStage 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(--sealpixel-color-bg);
21
+ background-image:
22
+ linear-gradient(45deg, var(--sealpixel-stage-checker-a) 25%, transparent 25%),
23
+ linear-gradient(-45deg, var(--sealpixel-stage-checker-a) 25%, transparent 25%),
24
+ linear-gradient(45deg, transparent 75%, var(--sealpixel-stage-checker-a) 75%),
25
+ linear-gradient(-45deg, transparent 75%, var(--sealpixel-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
+ /*
43
+ * Only a view the user did not drag glides: a fit, a double tap, a
44
+ * re-anchor after the geometry changed. A pinch, a wheel and a drag must
45
+ * track the pointer exactly, so they clear the class in the same update
46
+ * that writes the new transform and the image never lags behind a finger.
47
+ */
48
+ .content.glide {
49
+ transition: transform var(--sealpixel-duration-slow) var(--sealpixel-ease-out);
50
+ }
51
+ canvas {
52
+ display: block;
53
+ image-rendering: auto;
54
+ transform-origin: 50% 50%;
55
+ box-shadow: var(--sealpixel-shadow, none);
56
+ transition: clip-path var(--sealpixel-duration-slow) var(--sealpixel-ease-out);
57
+ }
58
+ svg.overlay {
59
+ position: absolute;
60
+ left: 0;
61
+ top: 0;
62
+ overflow: visible;
63
+ pointer-events: none;
64
+ }
65
+ svg.overlay [data-interactive] {
66
+ pointer-events: all;
67
+ }
68
+ .status {
69
+ position: absolute;
70
+ right: var(--sealpixel-space);
71
+ bottom: var(--sealpixel-space);
72
+ padding: 3px 9px;
73
+ border: 0;
74
+ border-radius: 999px;
75
+ background: color-mix(in srgb, var(--sealpixel-color-surface) 85%, transparent);
76
+ color: var(--sealpixel-color-fg-muted);
77
+ font: inherit;
78
+ font-size: 12px;
79
+ font-variant-numeric: tabular-nums;
80
+ backdrop-filter: blur(6px);
81
+ cursor: pointer;
82
+ transition:
83
+ color var(--sealpixel-transition),
84
+ background var(--sealpixel-transition),
85
+ opacity var(--sealpixel-duration) var(--sealpixel-ease);
86
+ }
87
+ /* Fitted means the button does nothing; it fades back rather than vanishing. */
88
+ .status:disabled {
89
+ cursor: default;
90
+ opacity: 0.55;
91
+ }
92
+ .status:not(:disabled):hover {
93
+ color: var(--sealpixel-color-fg);
94
+ background: color-mix(in srgb, var(--sealpixel-color-surface) 96%, transparent);
95
+ }
96
+ .status:focus-visible {
97
+ outline: 2px solid var(--sealpixel-color-focus);
98
+ outline-offset: 2px;
99
+ }
100
+ /*
101
+ * A full render is in flight. The dot breathes instead of sitting still,
102
+ * because a static dot is read as part of the image. It starts at the
103
+ * resting opacity, so a render that finishes within one frame does not
104
+ * flash.
105
+ */
106
+ .pending {
107
+ position: absolute;
108
+ left: var(--sealpixel-space);
109
+ top: var(--sealpixel-space);
110
+ width: 8px;
111
+ height: 8px;
112
+ border-radius: 50%;
113
+ background: var(--sealpixel-color-accent);
114
+ opacity: 0.8;
115
+ animation: sealpixel-pending 1.4s var(--sealpixel-ease) infinite;
116
+ }
117
+ @keyframes sealpixel-pending {
118
+ 50% {
119
+ opacity: 0.25;
120
+ transform: scale(0.7);
121
+ }
122
+ }
123
+ `,
124
+ ]; }
125
+ static { this.properties = {
126
+ preview: { attribute: false },
127
+ pending: { type: Boolean },
128
+ overlay: { attribute: false },
129
+ hintFilter: { attribute: false },
130
+ hintTransform: { attribute: false },
131
+ clip: { attribute: false },
132
+ zoom: { type: Number },
133
+ panEnabled: { type: Boolean },
134
+ t: { attribute: false },
135
+ }; }
136
+ constructor() {
137
+ super();
138
+ this.canvas = null;
139
+ this.drawnVersion = -1;
140
+ this.pan = { x: 0, y: 0 };
141
+ this.fitted = false;
142
+ /** Preview px → source px of the drawn preview; a change means the stage shows another view. */
143
+ this.viewMap = null;
144
+ this.lastTap = null;
145
+ /**
146
+ * True while the next transform should be animated. Every path that moves the
147
+ * view sets it, so it can never linger from an earlier action into a drag.
148
+ */
149
+ this.glide = false;
150
+ /**
151
+ * Set by a tool that wants the next preview to arrive as a movement rather
152
+ * than a cut. Crop uses it: leaving the tool is the moment the image really
153
+ * becomes the crop, and seeing it close is the only signal the user gets
154
+ * that anything happened.
155
+ */
156
+ this.glidePending = false;
157
+ this.resizeObserver = null;
158
+ this.gestures = new GestureController(this, {
159
+ drag: (e) => {
160
+ if (!this.panEnabled)
161
+ return;
162
+ this.panBy(e.dx, e.dy);
163
+ },
164
+ // A second tap close to the first fits the image again (double-tap, double-click).
165
+ tap: (e) => {
166
+ const now = performance.now();
167
+ const prev = this.lastTap;
168
+ if (prev &&
169
+ now - prev.at < DOUBLE_TAP_MS &&
170
+ Math.hypot(e.x - prev.x, e.y - prev.y) < DOUBLE_TAP_PX) {
171
+ this.lastTap = null;
172
+ this.fit(true);
173
+ return;
174
+ }
175
+ this.lastTap = { x: e.x, y: e.y, at: now };
176
+ },
177
+ pinch: (e) => this.zoomAround(e.scale, e.cx, e.cy, e.dx, e.dy),
178
+ wheelZoom: (e) => this.zoomAround(e.factor, e.cx, e.cy, 0, 0),
179
+ // Plain scrolling pans only while the image is larger than the view;
180
+ // otherwise the page scrolls as the user expects.
181
+ wheelPan: (e) => {
182
+ if (!this.overflows())
183
+ return false;
184
+ this.panBy(e.dx, e.dy);
185
+ return true;
186
+ },
187
+ });
188
+ this.preview = null;
189
+ this.pending = false;
190
+ this.overlay = null;
191
+ this.hintFilter = '';
192
+ this.hintTransform = '';
193
+ this.clip = '';
194
+ this.zoom = 1;
195
+ this.panEnabled = true;
196
+ this.t = (key) => key;
197
+ }
198
+ get geometry() {
199
+ return this.preview?.geometry ?? null;
200
+ }
201
+ get imageWidth() {
202
+ return this.preview?.image.width ?? 0;
203
+ }
204
+ get imageHeight() {
205
+ return this.preview?.image.height ?? 0;
206
+ }
207
+ /** Converts a client point into preview image pixels. */
208
+ toImage(clientX, clientY) {
209
+ const r = this.getBoundingClientRect();
210
+ return {
211
+ x: (clientX - r.left - this.pan.x) / this.zoom,
212
+ y: (clientY - r.top - this.pan.y) / this.zoom,
213
+ };
214
+ }
215
+ /** Asks for the next preview-driven view change to be animated, once. */
216
+ glideNextView() {
217
+ this.glidePending = true;
218
+ }
219
+ /** `glide` animates the move; a resize or a first load must not animate. */
220
+ fit(glide = false) {
221
+ const w = this.imageWidth;
222
+ const h = this.imageHeight;
223
+ if (!w || !h)
224
+ return;
225
+ this.glide = glide;
226
+ const r = this.getBoundingClientRect();
227
+ this.zoom = this.fitZoom();
228
+ this.pan = { x: (r.width - w * this.zoom) / 2, y: (r.height - h * this.zoom) / 2 };
229
+ this.fitted = true;
230
+ this.requestUpdate();
231
+ this.viewChanged();
232
+ }
233
+ fitZoom() {
234
+ const r = this.getBoundingClientRect();
235
+ const margin = 24;
236
+ const zoom = Math.min((r.width - margin) / this.imageWidth, (r.height - margin) / this.imageHeight, 4);
237
+ return Math.max(zoom, 0.01);
238
+ }
239
+ /**
240
+ * The preview now shows another view of the same source (a tool switched
241
+ * between cropped and uncropped, a resize changed the output). Keep the
242
+ * source pixel under the view centre where it was and keep its size on
243
+ * screen, instead of fitting and letting the image jump. A view that was
244
+ * fitted is fitted again only when the steady view would overflow.
245
+ */
246
+ reanchor(prev, next) {
247
+ const r = this.getBoundingClientRect();
248
+ const cx = r.width / 2;
249
+ const cy = r.height / 2;
250
+ const centre = apply(prev, (cx - this.pan.x) / this.zoom, (cy - this.pan.y) / this.zoom);
251
+ const q = apply(invert(next), centre.x, centre.y);
252
+ const zoom = Math.min(16, Math.max(0.05, (this.zoom * scaleOf(prev)) / scaleOf(next)));
253
+ const iw = this.imageWidth * zoom;
254
+ const ih = this.imageHeight * zoom;
255
+ if (this.fitted && (iw > r.width || ih > r.height)) {
256
+ this.fit();
257
+ return;
258
+ }
259
+ // No glide here on purpose: the point of re-anchoring is that the source
260
+ // region does not appear to move at all. Animating it would show a slide
261
+ // where the contract is that nothing happened.
262
+ this.glide = false;
263
+ this.zoom = zoom;
264
+ this.pan = this.clampPan({ x: cx - q.x * zoom, y: cy - q.y * zoom });
265
+ this.fitted = Math.abs(zoom - this.fitZoom()) < 1e-3;
266
+ this.requestUpdate();
267
+ this.viewChanged();
268
+ }
269
+ /** True when the zoomed image is larger than the view in at least one axis. */
270
+ overflows() {
271
+ const r = this.getBoundingClientRect();
272
+ return this.imageWidth * this.zoom > r.width || this.imageHeight * this.zoom > r.height;
273
+ }
274
+ /**
275
+ * Keeps the image in view: an image smaller than the view stays fully
276
+ * visible, a larger one never shows empty space beyond its edges.
277
+ */
278
+ clampPan(pan) {
279
+ const r = this.getBoundingClientRect();
280
+ const iw = this.imageWidth * this.zoom;
281
+ const ih = this.imageHeight * this.zoom;
282
+ if (!iw || !ih)
283
+ return pan;
284
+ const axis = (v, size, view) => size <= view ? clamp(v, 0, view - size) : clamp(v, view - size, 0);
285
+ return { x: axis(pan.x, iw, r.width), y: axis(pan.y, ih, r.height) };
286
+ }
287
+ panBy(dx, dy) {
288
+ const next = this.clampPan({ x: this.pan.x + dx, y: this.pan.y + dy });
289
+ if (next.x === this.pan.x && next.y === this.pan.y)
290
+ return;
291
+ this.glide = false;
292
+ this.glidePending = false;
293
+ this.pan = next;
294
+ this.requestUpdate();
295
+ this.viewChanged();
296
+ }
297
+ /** Zoom or pan changed: overlays scale their gizmos by zoom and must re-render. */
298
+ viewChanged() {
299
+ this.dispatchEvent(new CustomEvent('stage-view', { detail: { zoom: this.zoom, pan: this.pan } }));
300
+ }
301
+ connectedCallback() {
302
+ super.connectedCallback();
303
+ this.resizeObserver = new ResizeObserver(() => {
304
+ if (this.fitted)
305
+ this.fit();
306
+ });
307
+ this.resizeObserver.observe(this);
308
+ }
309
+ disconnectedCallback() {
310
+ super.disconnectedCallback();
311
+ this.resizeObserver?.disconnect();
312
+ }
313
+ firstUpdated() {
314
+ this.canvas = this.renderRoot.querySelector('canvas');
315
+ this.gestures.attach(this.renderRoot.querySelector('.viewport'));
316
+ }
317
+ updated(changed) {
318
+ if (changed.has('preview'))
319
+ this.draw();
320
+ }
321
+ draw() {
322
+ const canvas = this.canvas;
323
+ const preview = this.preview;
324
+ if (!canvas || !preview)
325
+ return;
326
+ const { image } = preview;
327
+ const sizeChanged = canvas.width !== image.width || canvas.height !== image.height;
328
+ if (sizeChanged) {
329
+ canvas.width = image.width;
330
+ canvas.height = image.height;
331
+ }
332
+ if (preview.version !== this.drawnVersion || sizeChanged) {
333
+ const ctx = canvas.getContext('2d');
334
+ if (ctx) {
335
+ const data = new Uint8ClampedArray(image.data.byteLength);
336
+ data.set(image.data);
337
+ ctx.putImageData(new ImageData(data, image.width, image.height), 0, 0);
338
+ }
339
+ this.drawnVersion = preview.version;
340
+ // A real preview landed: drop tier-A hints.
341
+ this.hintFilter = '';
342
+ this.hintTransform = '';
343
+ }
344
+ const nextMap = previewToSource(preview.viewGeometry);
345
+ const prevMap = this.viewMap;
346
+ this.viewMap = nextMap;
347
+ if (this.glidePending) {
348
+ // A tool asked to be seen. Fit rather than re-anchor: a crop that just
349
+ // took half the image away should end up filling the stage, which is
350
+ // what makes the move read as the crop closing.
351
+ this.glidePending = false;
352
+ this.fit(true);
353
+ }
354
+ else if (prevMap && !sameAffine(prevMap, nextMap))
355
+ this.reanchor(prevMap, nextMap);
356
+ else if (!prevMap || (!this.fitted && sizeChanged))
357
+ this.fit();
358
+ }
359
+ zoomAround(factor, cx, cy, dx, dy) {
360
+ const next = Math.min(16, Math.max(0.05, this.zoom * factor));
361
+ const k = next / this.zoom;
362
+ this.glide = false;
363
+ this.glidePending = false;
364
+ this.zoom = next;
365
+ this.pan = this.clampPan({
366
+ x: cx - (cx - this.pan.x) * k + dx,
367
+ y: cy - (cy - this.pan.y) * k + dy,
368
+ });
369
+ this.fitted = false;
370
+ this.requestUpdate();
371
+ this.viewChanged();
372
+ }
373
+ render() {
374
+ const w = this.imageWidth;
375
+ const h = this.imageHeight;
376
+ const transform = `translate(${this.pan.x}px, ${this.pan.y}px) scale(${this.zoom})`;
377
+ return html `
378
+ <div class="viewport" part="viewport">
379
+ <div class="content ${this.glide ? 'glide' : ''}" style="transform:${transform}">
380
+ <canvas
381
+ part="canvas"
382
+ width=${w}
383
+ height=${h}
384
+ style="filter:${this.hintFilter || 'none'};transform:${this.hintTransform || 'none'};clip-path:${this.clip || 'none'}"
385
+ role="img"
386
+ aria-label="preview"
387
+ ></canvas>
388
+ ${this.overlay
389
+ ? svg `<svg class="overlay" part="overlay" width=${w} height=${h} viewBox="0 0 ${w} ${h}">${this.overlay}</svg>`
390
+ : null}
391
+ </div>
392
+ </div>
393
+ ${this.pending ? html `<div class="pending" aria-hidden="true"></div>` : null}
394
+ <button
395
+ type="button"
396
+ class="status"
397
+ part="status"
398
+ ?disabled=${this.fitted}
399
+ title=${this.t('stage.fit')}
400
+ aria-label=${`${this.t('stage.zoom', { percent: Math.round(this.zoom * 100) })}. ${this.t('stage.fit')}`}
401
+ @click=${() => this.fit(true)}
402
+ >${Math.round(this.zoom * 100)}%</button>
403
+ `;
404
+ }
405
+ }
406
+ function clamp(v, lo, hi) {
407
+ return v < lo ? lo : v > hi ? hi : v;
408
+ }
409
+ //# sourceMappingURL=sealpixel-stage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sealpixel-stage.js","sourceRoot":"","sources":["../../src/components/sealpixel-stage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ import { LitElement } from 'lit';
2
+ /**
3
+ * A number with a floor. Where the dial is for a value the user feels their way
4
+ * to from a middle, this is for one they already know: a font size, a line
5
+ * width, a corner radius. It reads, it types, and it nudges by one, which is
6
+ * everything a magnitude needs and none of what a wheel is good at. It also
7
+ * costs about a fifth of the width.
8
+ *
9
+ * The value is a real `input[type=number]`, so the keyboard, the spinbutton
10
+ * role and the value semantics are the platform's. Holding a button repeats;
11
+ * holding Shift steps by ten.
12
+ *
13
+ * Emits `slide` while a held button is repeating (tier-A hints) and `commit`
14
+ * when the value settles, so a held button writes one operation and not forty.
15
+ */
16
+ export declare class SealpixelStepper extends LitElement {
17
+ static styles: import("lit").CSSResult[];
18
+ static properties: {
19
+ label: {
20
+ type: StringConstructor;
21
+ };
22
+ value: {
23
+ type: NumberConstructor;
24
+ };
25
+ min: {
26
+ type: NumberConstructor;
27
+ };
28
+ max: {
29
+ type: NumberConstructor;
30
+ };
31
+ step: {
32
+ type: NumberConstructor;
33
+ };
34
+ unit: {
35
+ type: StringConstructor;
36
+ };
37
+ decimals: {
38
+ type: NumberConstructor;
39
+ };
40
+ disabled: {
41
+ type: BooleanConstructor;
42
+ reflect: boolean;
43
+ };
44
+ };
45
+ label: string;
46
+ value: number;
47
+ min: number;
48
+ max: number;
49
+ step: number;
50
+ /** Shown after the number, unstyled: `px`, `%`. Empty for a bare ratio. */
51
+ unit: string;
52
+ /** Digits after the point, for both the field and the rounding. */
53
+ decimals: number;
54
+ disabled: boolean;
55
+ constructor();
56
+ render(): import("lit").TemplateResult<1>;
57
+ private timer;
58
+ private repeating;
59
+ private clamp;
60
+ private emit;
61
+ /** One step. `live` reports progress; the commit follows when the button goes up. */
62
+ private bump;
63
+ private hold;
64
+ private release;
65
+ private onChange;
66
+ private onKeydown;
67
+ disconnectedCallback(): void;
68
+ }
69
+ //# sourceMappingURL=sealpixel-stepper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sealpixel-stepper.d.ts","sourceRoot":"","sources":["../../src/components/sealpixel-stepper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAE,MAAM,KAAK,CAAC;AAO5C;;;;;;;;;;;;;GAaG;AACH,qBAAa,gBAAiB,SAAQ,UAAU;IAC9C,OAAgB,MAAM,4BAkGpB;IAEF,OAAgB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;MASxB;IAEM,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACrB,2EAA2E;IACnE,IAAI,EAAE,MAAM,CAAC;IACrB,mEAAmE;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;;IAcjB,MAAM;IA8Cf,OAAO,CAAC,KAAK,CAA8C;IAE3D,OAAO,CAAC,SAAS,CAAS;IAE1B,OAAO,CAAC,KAAK;IAMb,OAAO,CAAC,IAAI;IAMZ,qFAAqF;IACrF,OAAO,CAAC,IAAI;IAOZ,OAAO,CAAC,IAAI,CASV;IAEF,OAAO,CAAC,OAAO,CAWb;IAEF,OAAO,CAAC,QAAQ,CAMd;IAEF,OAAO,CAAC,SAAS,CAOf;IAEO,oBAAoB,IAAI,IAAI;CAItC"}