@hjmds/design-contracts 1.13.1 → 1.14.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 (117) hide show
  1. package/dist/agreement.d.ts +7 -1
  2. package/dist/agreement.d.ts.map +1 -1
  3. package/dist/agreement.js +14 -3
  4. package/dist/agreement.js.map +1 -1
  5. package/dist/behaviors.d.ts +1 -1
  6. package/dist/catalog.d.ts +4 -0
  7. package/dist/catalog.d.ts.map +1 -1
  8. package/dist/content-transition.d.ts +17 -0
  9. package/dist/content-transition.d.ts.map +1 -1
  10. package/dist/content-transition.js +21 -0
  11. package/dist/content-transition.js.map +1 -1
  12. package/dist/date-entry.d.ts +71 -0
  13. package/dist/date-entry.d.ts.map +1 -0
  14. package/dist/date-entry.js +78 -0
  15. package/dist/date-entry.js.map +1 -0
  16. package/dist/document-resource.d.ts +104 -0
  17. package/dist/document-resource.d.ts.map +1 -0
  18. package/dist/document-resource.js +77 -0
  19. package/dist/document-resource.js.map +1 -0
  20. package/dist/effect-surface.d.ts +6 -1
  21. package/dist/effect-surface.d.ts.map +1 -1
  22. package/dist/effect-surface.js +5 -2
  23. package/dist/effect-surface.js.map +1 -1
  24. package/dist/field-group.d.ts +47 -0
  25. package/dist/field-group.d.ts.map +1 -0
  26. package/dist/field-group.js +92 -0
  27. package/dist/field-group.js.map +1 -0
  28. package/dist/image.d.ts +17 -0
  29. package/dist/image.d.ts.map +1 -1
  30. package/dist/image.js +19 -0
  31. package/dist/image.js.map +1 -1
  32. package/dist/internal/effect-noise.d.ts +2 -0
  33. package/dist/internal/effect-noise.d.ts.map +1 -0
  34. package/dist/internal/effect-noise.js +4 -0
  35. package/dist/internal/effect-noise.js.map +1 -0
  36. package/dist/progressive-blur.d.ts +32 -0
  37. package/dist/progressive-blur.d.ts.map +1 -0
  38. package/dist/progressive-blur.js +28 -0
  39. package/dist/progressive-blur.js.map +1 -0
  40. package/dist/reference-controls.d.ts +32 -0
  41. package/dist/reference-controls.d.ts.map +1 -0
  42. package/dist/reference-controls.js +28 -0
  43. package/dist/reference-controls.js.map +1 -0
  44. package/dist/scroll-progress.d.ts +7 -1
  45. package/dist/scroll-progress.d.ts.map +1 -1
  46. package/dist/scroll-progress.js +21 -2
  47. package/dist/scroll-progress.js.map +1 -1
  48. package/dist/text-annotation.d.ts +43 -0
  49. package/dist/text-annotation.d.ts.map +1 -0
  50. package/dist/text-annotation.js +137 -0
  51. package/dist/text-annotation.js.map +1 -0
  52. package/dist/version.d.ts +1 -1
  53. package/dist/version.js +1 -1
  54. package/dist/version.js.map +1 -1
  55. package/docs/agreement.md +14 -0
  56. package/docs/dialog.md +16 -1
  57. package/docs/effect-surface.md +19 -3
  58. package/docs/generated/component-maturity.md +1 -1
  59. package/docs/generated/renderer-evidence.json +3 -3
  60. package/docs/generated/renderer-evidence.md +1 -1
  61. package/docs/generated/showcase-manifest.json +1 -1
  62. package/docs/image.md +17 -0
  63. package/docs/optional-adapters.md +25 -0
  64. package/docs/popover.md +15 -0
  65. package/docs/rating.md +6 -1
  66. package/docs/reference-controls.md +40 -0
  67. package/docs/task-list.md +15 -1
  68. package/docs/text-annotation.md +85 -0
  69. package/docs/usage/README.md +18 -1
  70. package/docs/usage/components/agreement.md +13 -3
  71. package/docs/usage/components/alert-dialog.md +7 -1
  72. package/docs/usage/components/avatar.md +33 -1
  73. package/docs/usage/components/card.md +7 -1
  74. package/docs/usage/components/carousel.md +5 -1
  75. package/docs/usage/components/chat-message.md +11 -1
  76. package/docs/usage/components/chip.md +5 -0
  77. package/docs/usage/components/content-transition.md +28 -4
  78. package/docs/usage/components/dialog.md +18 -1
  79. package/docs/usage/components/effect-surface.md +4 -2
  80. package/docs/usage/components/empty-state.md +10 -2
  81. package/docs/usage/components/field.md +23 -0
  82. package/docs/usage/components/form.md +6 -1
  83. package/docs/usage/components/image-comparison.md +82 -0
  84. package/docs/usage/components/image.md +81 -2
  85. package/docs/usage/components/keyboard-avoiding.md +6 -1
  86. package/docs/usage/components/link.md +3 -1
  87. package/docs/usage/components/list-row.md +3 -1
  88. package/docs/usage/components/list.md +6 -1
  89. package/docs/usage/components/message-composer.md +5 -0
  90. package/docs/usage/components/popover.md +7 -1
  91. package/docs/usage/components/progress.md +28 -0
  92. package/docs/usage/components/progressive-blur.md +113 -0
  93. package/docs/usage/components/rating.md +74 -0
  94. package/docs/usage/components/search-field.md +10 -0
  95. package/docs/usage/components/search-screen.md +5 -0
  96. package/docs/usage/components/segmented-control.md +17 -0
  97. package/docs/usage/components/statistic.md +11 -0
  98. package/docs/usage/components/tags-input.md +5 -0
  99. package/docs/usage/components/toast.md +5 -0
  100. package/docs/usage/components/upload-item.md +3 -1
  101. package/docs/usage/compositions/action-feedback.md +77 -0
  102. package/docs/usage/compositions/adaptive-content.md +81 -0
  103. package/docs/usage/compositions/context-toolbar.md +85 -0
  104. package/docs/usage/compositions/date-entry.md +108 -0
  105. package/docs/usage/compositions/document-resource.md +124 -0
  106. package/docs/usage/compositions/field-group.md +104 -0
  107. package/docs/usage/compositions/illustrated-outcome.md +91 -0
  108. package/docs/usage/compositions/live-list.md +104 -0
  109. package/docs/usage/compositions/optional-adapters.md +1 -1
  110. package/docs/usage/compositions/origin-dialog.md +108 -0
  111. package/docs/usage/compositions/selection-motion.md +73 -0
  112. package/docs/usage/compositions/texture-comparison.md +86 -0
  113. package/docs/usage/compositions/upload-recovery.md +80 -0
  114. package/docs/usage/compositions/video-dialog.md +100 -0
  115. package/docs/usage/screens/flow-onboarding.md +5 -3
  116. package/docs/usage/screens/product-bento.md +117 -0
  117. package/package.json +37 -1
@@ -0,0 +1,77 @@
1
+ /** Controlled UI only: hosts connect the existing action session to their verified results. */
2
+ export function resolveDocumentResourceControls(props) {
3
+ const resource = resolveDocumentResource(props.descriptor);
4
+ for (const key of ["preview", "previewLoading", "previewUnavailable", "retryPreview", "save", "saving", "started", "saved", "cancelled", "retrySave"]) {
5
+ text(props.labels[key], `labels.${key}`);
6
+ }
7
+ if (typeof props.onSave !== "function")
8
+ throw new TypeError("Document resource requires onSave");
9
+ for (const key of ["onPreview", "onRetryPreview", "onRetrySave"]) {
10
+ if (props[key] !== undefined && typeof props[key] !== "function")
11
+ throw new TypeError(`Document resource ${key} must be a function`);
12
+ }
13
+ if (resource.preview.status === "error" && resource.preview.retryable && !props.onRetryPreview)
14
+ throw new TypeError("Retryable preview requires onRetryPreview");
15
+ if (resource.save.status === "error" && resource.save.retryable && !props.onRetrySave)
16
+ throw new TypeError("Retryable save requires onRetrySave");
17
+ return resource;
18
+ }
19
+ function text(value, field) {
20
+ if (typeof value !== "string" || !value.trim())
21
+ throw new TypeError(`Document resource ${field} must be nonempty text`);
22
+ }
23
+ function state(value, states, field) {
24
+ if (!value || typeof value !== "object" || !("status" in value) || !states.includes(value.status)) {
25
+ throw new TypeError(`Document resource ${field} has an unsupported status`);
26
+ }
27
+ if (value.status === "error") {
28
+ const error = value;
29
+ text(error.message, `${field} error`);
30
+ // An uncertain native save can have committed. Retry permission is explicit,
31
+ // just like action-session; an error alone never authorizes another write.
32
+ if (typeof error.retryable !== "boolean")
33
+ throw new TypeError(`Document resource ${field} retryable must be boolean`);
34
+ }
35
+ }
36
+ export function resolveDocumentResource(descriptor) {
37
+ text(descriptor.id, "id");
38
+ text(descriptor.name, "name");
39
+ for (const key of ["formatLabel", "sizeLabel", "description"]) {
40
+ if (descriptor[key] !== undefined)
41
+ text(descriptor[key], key);
42
+ }
43
+ for (const key of ["disabled", "saveDisabled"]) {
44
+ if (descriptor[key] !== undefined && typeof descriptor[key] !== "boolean") {
45
+ throw new TypeError(`Document resource ${key} must be boolean`);
46
+ }
47
+ }
48
+ state(descriptor.preview, ["none", "loading", "ready", "error"], "preview");
49
+ state(descriptor.save, ["idle", "pending", "started", "saved", "cancelled", "error"], "save");
50
+ const disabled = descriptor.disabled ?? false;
51
+ // Utilverse requires viewing the output before accepting export; whole-card disabling
52
+ // would prevent that review. The host owns the requirement, not a fake pending state.
53
+ const saveDisabled = disabled || descriptor.saveDisabled === true;
54
+ const preview = Object.freeze({ ...descriptor.preview });
55
+ const save = Object.freeze({ ...descriptor.save });
56
+ return Object.freeze({
57
+ id: descriptor.id,
58
+ name: descriptor.name,
59
+ // Preserve product formatting and order; neither URL extensions nor inferred
60
+ // byte counts are reliable metadata for remote/signed/local resources.
61
+ metadata: Object.freeze([descriptor.formatLabel, descriptor.sizeLabel].filter((value) => value !== undefined)),
62
+ description: descriptor.description,
63
+ disabled,
64
+ saveDisabled,
65
+ preview,
66
+ save,
67
+ canPreview: !disabled && preview.status === "ready",
68
+ canRetryPreview: !disabled && preview.status === "error" && preview.retryable,
69
+ // Keep failures on the explicit retry path. A second ordinary save must not
70
+ // bypass a host's retryable=false policy after an ambiguous OS response.
71
+ canSave: !saveDisabled && save.status !== "pending" && save.status !== "error",
72
+ canRetrySave: !saveDisabled && save.status === "error" && save.retryable,
73
+ // Browser anchor activation proves initiation, not file-system completion.
74
+ saved: save.status === "saved",
75
+ });
76
+ }
77
+ //# sourceMappingURL=document-resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-resource.js","sourceRoot":"","sources":["../src/document-resource.ts"],"names":[],"mappings":"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","sourcesContent":["/** Candidate composition contract; file access, transfer and receipt verification stay with the host. */\nexport type DocumentPreviewState =\n | Readonly<{ status: \"none\" | \"loading\" | \"ready\" }>\n | Readonly<{ status: \"error\"; message: string; retryable: boolean }>;\n\nexport type DocumentSaveState =\n | Readonly<{ status: \"idle\" | \"pending\" | \"started\" | \"saved\" | \"cancelled\" }>\n | Readonly<{ status: \"error\"; message: string; retryable: boolean }>;\n\nexport type DocumentResourceDescriptor = Readonly<{\n /** Include a revision when reusing a resource id for different file contents. */\n id: string;\n name: string;\n formatLabel?: string;\n sizeLabel?: string;\n description?: string;\n disabled?: boolean;\n /** Product review/permission gates may block export while keeping preview accessible. */\n saveDisabled?: boolean;\n preview: DocumentPreviewState;\n save: DocumentSaveState;\n}>;\n\nexport type DocumentResourceLabels = Readonly<{\n preview: string;\n previewLoading: string;\n previewUnavailable: string;\n retryPreview: string;\n save: string;\n saving: string;\n started: string;\n saved: string;\n cancelled: string;\n retrySave: string;\n}>;\n\nexport type DocumentResourceControls = Readonly<{\n descriptor: DocumentResourceDescriptor;\n labels: DocumentResourceLabels;\n onPreview?: () => void;\n onRetryPreview?: () => void;\n onSave: () => void;\n onRetrySave?: () => void;\n}>;\n\n/** Controlled UI only: hosts connect the existing action session to their verified results. */\nexport function resolveDocumentResourceControls(props: DocumentResourceControls) {\n const resource = resolveDocumentResource(props.descriptor);\n for (const key of [\"preview\", \"previewLoading\", \"previewUnavailable\", \"retryPreview\", \"save\", \"saving\", \"started\", \"saved\", \"cancelled\", \"retrySave\"] as const) {\n text(props.labels[key], `labels.${key}`);\n }\n if (typeof props.onSave !== \"function\") throw new TypeError(\"Document resource requires onSave\");\n for (const key of [\"onPreview\", \"onRetryPreview\", \"onRetrySave\"] as const) {\n if (props[key] !== undefined && typeof props[key] !== \"function\") throw new TypeError(`Document resource ${key} must be a function`);\n }\n if (resource.preview.status === \"error\" && resource.preview.retryable && !props.onRetryPreview) throw new TypeError(\"Retryable preview requires onRetryPreview\");\n if (resource.save.status === \"error\" && resource.save.retryable && !props.onRetrySave) throw new TypeError(\"Retryable save requires onRetrySave\");\n return resource;\n}\n\nfunction text(value: unknown, field: string): asserts value is string {\n if (typeof value !== \"string\" || !value.trim()) throw new TypeError(`Document resource ${field} must be nonempty text`);\n}\n\nfunction state(value: unknown, states: readonly string[], field: string): void {\n if (!value || typeof value !== \"object\" || !(\"status\" in value) || !states.includes(value.status as string)) {\n throw new TypeError(`Document resource ${field} has an unsupported status`);\n }\n if (value.status === \"error\") {\n const error = value as { message?: unknown; retryable?: unknown };\n text(error.message, `${field} error`);\n // An uncertain native save can have committed. Retry permission is explicit,\n // just like action-session; an error alone never authorizes another write.\n if (typeof error.retryable !== \"boolean\") throw new TypeError(`Document resource ${field} retryable must be boolean`);\n }\n}\n\nexport function resolveDocumentResource(descriptor: DocumentResourceDescriptor) {\n text(descriptor.id, \"id\");\n text(descriptor.name, \"name\");\n for (const key of [\"formatLabel\", \"sizeLabel\", \"description\"] as const) {\n if (descriptor[key] !== undefined) text(descriptor[key], key);\n }\n for (const key of [\"disabled\", \"saveDisabled\"] as const) {\n if (descriptor[key] !== undefined && typeof descriptor[key] !== \"boolean\") {\n throw new TypeError(`Document resource ${key} must be boolean`);\n }\n }\n state(descriptor.preview, [\"none\", \"loading\", \"ready\", \"error\"], \"preview\");\n state(descriptor.save, [\"idle\", \"pending\", \"started\", \"saved\", \"cancelled\", \"error\"], \"save\");\n const disabled = descriptor.disabled ?? false;\n // Utilverse requires viewing the output before accepting export; whole-card disabling\n // would prevent that review. The host owns the requirement, not a fake pending state.\n const saveDisabled = disabled || descriptor.saveDisabled === true;\n const preview = Object.freeze({ ...descriptor.preview });\n const save = Object.freeze({ ...descriptor.save });\n return Object.freeze({\n id: descriptor.id,\n name: descriptor.name,\n // Preserve product formatting and order; neither URL extensions nor inferred\n // byte counts are reliable metadata for remote/signed/local resources.\n metadata: Object.freeze([descriptor.formatLabel, descriptor.sizeLabel].filter((value): value is string => value !== undefined)),\n description: descriptor.description,\n disabled,\n saveDisabled,\n preview,\n save,\n canPreview: !disabled && preview.status === \"ready\",\n canRetryPreview: !disabled && preview.status === \"error\" && preview.retryable,\n // Keep failures on the explicit retry path. A second ordinary save must not\n // bypass a host's retryable=false policy after an ambiguous OS response.\n canSave: !saveDisabled && save.status !== \"pending\" && save.status !== \"error\",\n canRetrySave: !saveDisabled && save.status === \"error\" && save.retryable,\n // Browser anchor activation proves initiation, not file-system completion.\n saved: save.status === \"saved\",\n });\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { type ColorReference } from "./color-references.js";
2
- export type EffectLayer = "mesh" | "glow" | "grain";
2
+ export type EffectLayer = "mesh" | "glow" | "grain" | "noise";
3
3
  export type EffectSurfaceDescriptor = Readonly<{
4
4
  layers?: readonly EffectLayer[];
5
5
  seed?: string;
@@ -15,6 +15,11 @@ export declare function resolveEffectSurface(descriptor?: EffectSurfaceDescripto
15
15
  period: number;
16
16
  active: boolean;
17
17
  colors: readonly [ColorReference, ColorReference, ColorReference];
18
+ noise: {
19
+ uri: string;
20
+ size: number;
21
+ offset: number;
22
+ } | undefined;
18
23
  points: {
19
24
  x: number;
20
25
  y: number;
@@ -1 +1 @@
1
- {"version":3,"file":"effect-surface.d.ts","sourceRoot":"","sources":["../src/effect-surface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACxE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AACpD,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,MAAM,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,SAAS,CAAC,cAAc,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;CACpE,CAAC,CAAC;AACH,wBAAgB,oBAAoB,CAAC,UAAU,GAAE,uBAA4B;;;;;;;;;;;;;;;;EAkB5E"}
1
+ {"version":3,"file":"effect-surface.d.ts","sourceRoot":"","sources":["../src/effect-surface.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACxE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAC9D,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,MAAM,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,SAAS,CAAC,cAAc,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;CACpE,CAAC,CAAC;AACH,wBAAgB,oBAAoB,CAAC,UAAU,GAAE,uBAA4B;;;;;;;;;;;;;;;;;;;;;EAoB5E"}
@@ -1,8 +1,9 @@
1
+ import { effectNoiseTile } from "./internal/effect-noise.js";
1
2
  import { themeColor } from "./color-references.js";
2
3
  export function resolveEffectSurface(descriptor = {}) {
3
4
  const layers = descriptor.layers ?? ["mesh"];
4
- if (!layers.length || layers.length > 3 || new Set(layers).size !== layers.length || layers.some(layer => !["mesh", "glow", "grain"].includes(layer)))
5
- throw new TypeError("Choose one to three unique effect layers");
5
+ if (!layers.length || layers.length > 4 || new Set(layers).size !== layers.length || layers.some(layer => !["mesh", "glow", "grain", "noise"].includes(layer)))
6
+ throw new TypeError("Choose one to four unique effect layers");
6
7
  const intensity = descriptor.intensity ?? 0.22;
7
8
  if (!Number.isFinite(intensity) || intensity < 0 || intensity > 1)
8
9
  throw new RangeError("Effect intensity must be between 0 and 1");
@@ -21,6 +22,8 @@ export function resolveEffectSurface(descriptor = {}) {
21
22
  const points = Array.from({ length: 32 }, () => ({ x: Math.round(random() * 100), y: Math.round(random() * 100), radius: 0.2 + random() * 0.5 }));
22
23
  return { layers, intensity, period, active: descriptor.active ?? false,
23
24
  colors: descriptor.colors ?? [themeColor("primary"), themeColor("contentBrand"), themeColor("surfaceAccent")],
25
+ // Fixed periodic raster is shared across hosts; seed shifts its phase, not its pixels.
26
+ noise: layers.includes("noise") ? { uri: effectNoiseTile, size: 64, offset: points[0].x / 4 } : undefined,
24
27
  points, anchors: points.slice(0, 3) };
25
28
  }
26
29
  //# sourceMappingURL=effect-surface.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"effect-surface.js","sourceRoot":"","sources":["../src/effect-surface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAuB,MAAM,uBAAuB,CAAC;AAWxE,MAAM,UAAU,oBAAoB,CAAC,aAAsC,EAAE;IAC3E,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAC;IACvN,MAAM,SAAS,GAAG,UAAU,CAAC,SAAS,IAAI,IAAI,CAAC;IAC/C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,IAAI,SAAS,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,0CAA0C,CAAC,CAAC;IACpI,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,IAAI,EAAE,CAAC;IACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,GAAG;QAAE,MAAM,IAAI,UAAU,CAAC,iDAAiD,CAAC,CAAC;IACpI,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,IAAI,KAAK,CAAC;IACtC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,+BAA+B,CAAC,CAAC;IACxG,6EAA6E;IAC7E,0EAA0E;IAC1E,IAAI,KAAK,GAAG,UAAU,CAAC;IACvB,KAAK,MAAM,SAAS,IAAI,IAAI;QAAE,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC,CAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnG,MAAM,MAAM,GAAG,GAAG,EAAE,GAAG,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IAC5G,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,MAAM,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;IAClJ,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,IAAI,KAAK;QACpE,MAAM,EAAE,UAAU,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,eAAe,CAAC,CAAU;QACtH,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AAC1C,CAAC","sourcesContent":["import { themeColor, type ColorReference } from \"./color-references.js\";\nexport type EffectLayer = \"mesh\" | \"glow\" | \"grain\";\nexport type EffectSurfaceDescriptor = Readonly<{\n layers?: readonly EffectLayer[];\n seed?: string;\n intensity?: number;\n /** One slow cycle in seconds; decorative motion is off unless explicitly active. */\n period?: number;\n active?: boolean;\n colors?: readonly [ColorReference, ColorReference, ColorReference];\n}>;\nexport function resolveEffectSurface(descriptor: EffectSurfaceDescriptor = {}) {\n const layers = descriptor.layers ?? [\"mesh\"];\n if (!layers.length || layers.length > 3 || new Set(layers).size !== layers.length || layers.some(layer => ![\"mesh\", \"glow\", \"grain\"].includes(layer))) throw new TypeError(\"Choose one to three unique effect layers\");\n const intensity = descriptor.intensity ?? 0.22;\n if (!Number.isFinite(intensity) || intensity < 0 || intensity > 1) throw new RangeError(\"Effect intensity must be between 0 and 1\");\n const period = descriptor.period ?? 12;\n if (!Number.isFinite(period) || period < 2 || period > 120) throw new RangeError(\"Effect period must be between 2 and 120 seconds\");\n const seed = descriptor.seed ?? \"hjm\";\n if (typeof seed !== \"string\" || seed.length === 0) throw new TypeError(\"Effect seed must not be empty\");\n // Small deterministic spatial sampling keeps SSR/native geometry consistent;\n // it is decorative randomness, not a security or account identifier hash.\n let state = 2166136261;\n for (const character of seed) state = Math.imul(state ^ character.codePointAt(0)!, 16777619) >>> 0;\n const random = () => { state = (Math.imul(state, 1664525) + 1013904223) >>> 0; return state / 4294967296; };\n const points = Array.from({ length: 32 }, () => ({ x: Math.round(random() * 100), y: Math.round(random() * 100), radius: 0.2 + random() * 0.5 }));\n return { layers, intensity, period, active: descriptor.active ?? false,\n colors: descriptor.colors ?? [themeColor(\"primary\"), themeColor(\"contentBrand\"), themeColor(\"surfaceAccent\")] as const,\n points, anchors: points.slice(0, 3) };\n}\n"]}
1
+ {"version":3,"file":"effect-surface.js","sourceRoot":"","sources":["../src/effect-surface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAuB,MAAM,uBAAuB,CAAC;AAWxE,MAAM,UAAU,oBAAoB,CAAC,aAAsC,EAAE;IAC3E,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,yCAAyC,CAAC,CAAC;IAC/N,MAAM,SAAS,GAAG,UAAU,CAAC,SAAS,IAAI,IAAI,CAAC;IAC/C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,IAAI,SAAS,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,0CAA0C,CAAC,CAAC;IACpI,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,IAAI,EAAE,CAAC;IACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,GAAG;QAAE,MAAM,IAAI,UAAU,CAAC,iDAAiD,CAAC,CAAC;IACpI,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,IAAI,KAAK,CAAC;IACtC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,+BAA+B,CAAC,CAAC;IACxG,6EAA6E;IAC7E,0EAA0E;IAC1E,IAAI,KAAK,GAAG,UAAU,CAAC;IACvB,KAAK,MAAM,SAAS,IAAI,IAAI;QAAE,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC,CAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnG,MAAM,MAAM,GAAG,GAAG,EAAE,GAAG,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IAC5G,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,MAAM,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;IAClJ,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,IAAI,KAAK;QACpE,MAAM,EAAE,UAAU,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,eAAe,CAAC,CAAU;QACtH,uFAAuF;QACvF,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;QAC1G,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AAC1C,CAAC","sourcesContent":["import { effectNoiseTile } from \"./internal/effect-noise.js\";\nimport { themeColor, type ColorReference } from \"./color-references.js\";\nexport type EffectLayer = \"mesh\" | \"glow\" | \"grain\" | \"noise\";\nexport type EffectSurfaceDescriptor = Readonly<{\n layers?: readonly EffectLayer[];\n seed?: string;\n intensity?: number;\n /** One slow cycle in seconds; decorative motion is off unless explicitly active. */\n period?: number;\n active?: boolean;\n colors?: readonly [ColorReference, ColorReference, ColorReference];\n}>;\nexport function resolveEffectSurface(descriptor: EffectSurfaceDescriptor = {}) {\n const layers = descriptor.layers ?? [\"mesh\"];\n if (!layers.length || layers.length > 4 || new Set(layers).size !== layers.length || layers.some(layer => ![\"mesh\", \"glow\", \"grain\", \"noise\"].includes(layer))) throw new TypeError(\"Choose one to four unique effect layers\");\n const intensity = descriptor.intensity ?? 0.22;\n if (!Number.isFinite(intensity) || intensity < 0 || intensity > 1) throw new RangeError(\"Effect intensity must be between 0 and 1\");\n const period = descriptor.period ?? 12;\n if (!Number.isFinite(period) || period < 2 || period > 120) throw new RangeError(\"Effect period must be between 2 and 120 seconds\");\n const seed = descriptor.seed ?? \"hjm\";\n if (typeof seed !== \"string\" || seed.length === 0) throw new TypeError(\"Effect seed must not be empty\");\n // Small deterministic spatial sampling keeps SSR/native geometry consistent;\n // it is decorative randomness, not a security or account identifier hash.\n let state = 2166136261;\n for (const character of seed) state = Math.imul(state ^ character.codePointAt(0)!, 16777619) >>> 0;\n const random = () => { state = (Math.imul(state, 1664525) + 1013904223) >>> 0; return state / 4294967296; };\n const points = Array.from({ length: 32 }, () => ({ x: Math.round(random() * 100), y: Math.round(random() * 100), radius: 0.2 + random() * 0.5 }));\n return { layers, intensity, period, active: descriptor.active ?? false,\n colors: descriptor.colors ?? [themeColor(\"primary\"), themeColor(\"contentBrand\"), themeColor(\"surfaceAccent\")] as const,\n // Fixed periodic raster is shared across hosts; seed shifts its phase, not its pixels.\n noise: layers.includes(\"noise\") ? { uri: effectNoiseTile, size: 64, offset: points[0]!.x / 4 } : undefined,\n points, anchors: points.slice(0, 3) };\n}\n"]}
@@ -0,0 +1,47 @@
1
+ /** Related-input grouping contract. Values and submit/validation timing remain product-owned. */
2
+ export type FieldGroupMember = Readonly<{
3
+ id: string;
4
+ label: string;
5
+ description?: string;
6
+ error?: string;
7
+ disabled?: boolean;
8
+ }>;
9
+ export type FieldGroupDescriptor = Readonly<{
10
+ label: string;
11
+ description?: string;
12
+ disabled?: boolean;
13
+ /** An empty fieldIds list is a group-only error, not a reason to mark every field invalid. */
14
+ error?: Readonly<{
15
+ message: string;
16
+ fieldIds: readonly string[];
17
+ }>;
18
+ fields: readonly FieldGroupMember[];
19
+ }>;
20
+ export type ResolvedFieldGroupMember = Readonly<{
21
+ id: string;
22
+ label: string;
23
+ groupLabel: string;
24
+ disabled: boolean;
25
+ invalid: boolean;
26
+ /** Renderers associate these messages without replacing a field's own help or error. */
27
+ support: readonly Readonly<{
28
+ scope: "group" | "field";
29
+ kind: "description" | "error";
30
+ text: string;
31
+ }>[];
32
+ }>;
33
+ /** Late native events and retained callbacks must use the current committed group policy. */
34
+ export declare function createFieldGroupEditSession(fields: readonly ResolvedFieldGroupMember[]): {
35
+ update(next: readonly ResolvedFieldGroupMember[]): void;
36
+ /** Effect cleanup blocks events without treating Strict Mode replay as field removal. */
37
+ suspend(): void;
38
+ guard<Args extends unknown[]>(id: string, callback: (...args: Args) => void): (...args: Args) => void;
39
+ };
40
+ export declare function resolveFieldGroup(descriptor: FieldGroupDescriptor): Readonly<{
41
+ label: string;
42
+ description: string | undefined;
43
+ error: string | undefined;
44
+ disabled: boolean;
45
+ fields: readonly ResolvedFieldGroupMember[];
46
+ }>;
47
+ //# sourceMappingURL=field-group.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-group.d.ts","sourceRoot":"","sources":["../src/field-group.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8FAA8F;IAC9F,KAAK,CAAC,EAAE,QAAQ,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACnE,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAC;CACrC,CAAC,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,wFAAwF;IACxF,OAAO,EAAE,SAAS,QAAQ,CAAC;QAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;QAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAC;CACzG,CAAC,CAAC;AAMH,6FAA6F;AAC7F,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,SAAS,wBAAwB,EAAE;iBAItE,SAAS,wBAAwB,EAAE;IAQhD,yFAAyF;;UAEnF,IAAI,SAAS,OAAO,EAAE,MAAM,MAAM,YAAY,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI;EAOxG;AAUD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,oBAAoB,GAAG,QAAQ,CAAC;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,SAAS,wBAAwB,EAAE,CAAC;CAC7C,CAAC,CA2CD"}
@@ -0,0 +1,92 @@
1
+ function requireText(value, name) {
2
+ if (typeof value !== "string" || !value.trim())
3
+ throw new TypeError(`Field group ${name} must be nonempty text`);
4
+ }
5
+ /** Late native events and retained callbacks must use the current committed group policy. */
6
+ export function createFieldGroupEditSession(fields) {
7
+ let enabled = new Set(fields.filter(field => !field.disabled).map(field => field.id));
8
+ const members = new Map(fields.map(field => [field.id, {}]));
9
+ return {
10
+ update(next) {
11
+ const current = new Set(next.map(field => field.id));
12
+ // A reused id is a new mounted field after removal. Checking only enabled ids
13
+ // would revive queued events from the old field and overwrite the new draft.
14
+ for (const id of members.keys())
15
+ if (!current.has(id))
16
+ members.delete(id);
17
+ for (const id of current)
18
+ if (!members.has(id))
19
+ members.set(id, {});
20
+ enabled = new Set(next.filter(field => !field.disabled).map(field => field.id));
21
+ },
22
+ /** Effect cleanup blocks events without treating Strict Mode replay as field removal. */
23
+ suspend() { enabled.clear(); },
24
+ guard(id, callback) {
25
+ // A new host binds during render, before its committed policy enables it.
26
+ if (!members.has(id))
27
+ members.set(id, {});
28
+ const member = members.get(id);
29
+ return (...args) => { if (members.get(id) === member && enabled.has(id))
30
+ callback(...args); };
31
+ },
32
+ };
33
+ }
34
+ function optionalText(value, name) {
35
+ if (value !== undefined)
36
+ requireText(value, name);
37
+ }
38
+ function optionalBoolean(value) {
39
+ if (value !== undefined && typeof value !== "boolean")
40
+ throw new TypeError("Field group disabled must be boolean");
41
+ }
42
+ export function resolveFieldGroup(descriptor) {
43
+ requireText(descriptor.label, "label");
44
+ optionalText(descriptor.description, "description");
45
+ optionalBoolean(descriptor.disabled);
46
+ if (!Array.isArray(descriptor.fields))
47
+ throw new TypeError("Field group fields must be an array");
48
+ const ids = new Set();
49
+ for (const field of descriptor.fields) {
50
+ requireText(field.id, "field id");
51
+ requireText(field.label, "field label");
52
+ optionalText(field.description, "field description");
53
+ optionalText(field.error, "field error");
54
+ optionalBoolean(field.disabled);
55
+ if (ids.has(field.id))
56
+ throw new TypeError("Field group field ids must be unique");
57
+ ids.add(field.id);
58
+ }
59
+ if (descriptor.error !== undefined) {
60
+ requireText(descriptor.error.message, "error");
61
+ const affected = descriptor.error.fieldIds;
62
+ // A typo or removed field must not silently lose its validation feedback.
63
+ // Products update the visible error with their dynamic field list atomically.
64
+ if (!Array.isArray(affected) || new Set(affected).size !== affected.length || affected.some(id => !ids.has(id))) {
65
+ throw new TypeError("Field group error must reference distinct current field ids");
66
+ }
67
+ }
68
+ const disabled = descriptor.disabled ?? false;
69
+ return Object.freeze({
70
+ label: descriptor.label,
71
+ description: descriptor.description,
72
+ error: descriptor.error?.message,
73
+ disabled,
74
+ fields: Object.freeze(descriptor.fields.map(field => {
75
+ const groupError = descriptor.error?.fieldIds.includes(field.id) ? descriptor.error.message : undefined;
76
+ const support = [];
77
+ if (descriptor.description !== undefined)
78
+ support.push({ scope: "group", kind: "description", text: descriptor.description });
79
+ if (field.description !== undefined)
80
+ support.push({ scope: "field", kind: "description", text: field.description });
81
+ if (field.error !== undefined)
82
+ support.push({ scope: "field", kind: "error", text: field.error });
83
+ if (groupError !== undefined)
84
+ support.push({ scope: "group", kind: "error", text: groupError });
85
+ return Object.freeze({ id: field.id, label: field.label, groupLabel: descriptor.label,
86
+ // Group unlock restores independently disabled members; it never enables them.
87
+ disabled: disabled || (field.disabled ?? false), invalid: field.error !== undefined || groupError !== undefined,
88
+ support: Object.freeze(support.map(message => Object.freeze(message))) });
89
+ })),
90
+ });
91
+ }
92
+ //# sourceMappingURL=field-group.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-group.js","sourceRoot":"","sources":["../src/field-group.ts"],"names":[],"mappings":"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","sourcesContent":["/** Related-input grouping contract. Values and submit/validation timing remain product-owned. */\nexport type FieldGroupMember = Readonly<{\n id: string;\n label: string;\n description?: string;\n error?: string;\n disabled?: boolean;\n}>;\n\nexport type FieldGroupDescriptor = Readonly<{\n label: string;\n description?: string;\n disabled?: boolean;\n /** An empty fieldIds list is a group-only error, not a reason to mark every field invalid. */\n error?: Readonly<{ message: string; fieldIds: readonly string[] }>;\n fields: readonly FieldGroupMember[];\n}>;\n\nexport type ResolvedFieldGroupMember = Readonly<{\n id: string;\n label: string;\n groupLabel: string;\n disabled: boolean;\n invalid: boolean;\n /** Renderers associate these messages without replacing a field's own help or error. */\n support: readonly Readonly<{ scope: \"group\" | \"field\"; kind: \"description\" | \"error\"; text: string }>[];\n}>;\n\nfunction requireText(value: unknown, name: string): asserts value is string {\n if (typeof value !== \"string\" || !value.trim()) throw new TypeError(`Field group ${name} must be nonempty text`);\n}\n\n/** Late native events and retained callbacks must use the current committed group policy. */\nexport function createFieldGroupEditSession(fields: readonly ResolvedFieldGroupMember[]) {\n let enabled = new Set(fields.filter(field => !field.disabled).map(field => field.id));\n const members = new Map(fields.map(field => [field.id, {}]));\n return {\n update(next: readonly ResolvedFieldGroupMember[]) {\n const current = new Set(next.map(field => field.id));\n // A reused id is a new mounted field after removal. Checking only enabled ids\n // would revive queued events from the old field and overwrite the new draft.\n for (const id of members.keys()) if (!current.has(id)) members.delete(id);\n for (const id of current) if (!members.has(id)) members.set(id, {});\n enabled = new Set(next.filter(field => !field.disabled).map(field => field.id));\n },\n /** Effect cleanup blocks events without treating Strict Mode replay as field removal. */\n suspend() { enabled.clear(); },\n guard<Args extends unknown[]>(id: string, callback: (...args: Args) => void): (...args: Args) => void {\n // A new host binds during render, before its committed policy enables it.\n if (!members.has(id)) members.set(id, {});\n const member = members.get(id);\n return (...args) => { if (members.get(id) === member && enabled.has(id)) callback(...args); };\n },\n };\n}\n\nfunction optionalText(value: unknown, name: string): void {\n if (value !== undefined) requireText(value, name);\n}\n\nfunction optionalBoolean(value: unknown): void {\n if (value !== undefined && typeof value !== \"boolean\") throw new TypeError(\"Field group disabled must be boolean\");\n}\n\nexport function resolveFieldGroup(descriptor: FieldGroupDescriptor): Readonly<{\n label: string;\n description: string | undefined;\n error: string | undefined;\n disabled: boolean;\n fields: readonly ResolvedFieldGroupMember[];\n}> {\n requireText(descriptor.label, \"label\");\n optionalText(descriptor.description, \"description\");\n optionalBoolean(descriptor.disabled);\n if (!Array.isArray(descriptor.fields)) throw new TypeError(\"Field group fields must be an array\");\n const ids = new Set<string>();\n for (const field of descriptor.fields) {\n requireText(field.id, \"field id\");\n requireText(field.label, \"field label\");\n optionalText(field.description, \"field description\");\n optionalText(field.error, \"field error\");\n optionalBoolean(field.disabled);\n if (ids.has(field.id)) throw new TypeError(\"Field group field ids must be unique\");\n ids.add(field.id);\n }\n if (descriptor.error !== undefined) {\n requireText(descriptor.error.message, \"error\");\n const affected = descriptor.error.fieldIds;\n // A typo or removed field must not silently lose its validation feedback.\n // Products update the visible error with their dynamic field list atomically.\n if (!Array.isArray(affected) || new Set(affected).size !== affected.length || affected.some(id => !ids.has(id))) {\n throw new TypeError(\"Field group error must reference distinct current field ids\");\n }\n }\n const disabled = descriptor.disabled ?? false;\n return Object.freeze({\n label: descriptor.label,\n description: descriptor.description,\n error: descriptor.error?.message,\n disabled,\n fields: Object.freeze(descriptor.fields.map(field => {\n const groupError = descriptor.error?.fieldIds.includes(field.id) ? descriptor.error.message : undefined;\n const support: ResolvedFieldGroupMember[\"support\"][number][] = [];\n if (descriptor.description !== undefined) support.push({ scope: \"group\", kind: \"description\", text: descriptor.description });\n if (field.description !== undefined) support.push({ scope: \"field\", kind: \"description\", text: field.description });\n if (field.error !== undefined) support.push({ scope: \"field\", kind: \"error\", text: field.error });\n if (groupError !== undefined) support.push({ scope: \"group\", kind: \"error\", text: groupError });\n return Object.freeze({ id: field.id, label: field.label, groupLabel: descriptor.label,\n // Group unlock restores independently disabled members; it never enables them.\n disabled: disabled || (field.disabled ?? false), invalid: field.error !== undefined || groupError !== undefined,\n support: Object.freeze(support.map(message => Object.freeze(message))) });\n })),\n });\n}\n"]}
package/dist/image.d.ts CHANGED
@@ -1,5 +1,22 @@
1
1
  export type ImageFit = "cover" | "contain" | "fill";
2
2
  export type ImageLoadStatus = "idle" | "loading" | "loaded" | "error";
3
+ export type ImageInspectionMode = "fit" | "double" | "pixels";
4
+ /** Exact image inspection sizes use layout units, not device physical pixels (Utilverse ADR-0020). */
5
+ export declare function resolveImageInspectionGeometry(image: Readonly<{
6
+ width: number;
7
+ height: number;
8
+ }>, viewport: Readonly<{
9
+ width: number;
10
+ height: number;
11
+ }>, mode: ImageInspectionMode): {
12
+ scale: number;
13
+ width: number;
14
+ height: number;
15
+ panBounds: {
16
+ x: number;
17
+ y: number;
18
+ };
19
+ };
3
20
  export declare const imageDefaults: {
4
21
  readonly fit: "cover";
5
22
  };
@@ -1 +1 @@
1
- {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AACpD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEtE,eAAO,MAAM,aAAa;;CAEsB,CAAC;AAEjD,KAAK,eAAe,GAAG,QAAQ,CAAC;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,QAAQ,CAAC;CAChB,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,MAAM,yBAAyB,GAAG,eAAe,GACrD,QAAQ,CAAC;IAAE,UAAU,CAAC,EAAE,IAAI,CAAC;IAAC,kBAAkB,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAE9D,MAAM,MAAM,0BAA0B,GAAG,eAAe,GACtD,QAAQ,CAAC;IAAE,UAAU,EAAE,KAAK,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE9D,MAAM,MAAM,eAAe,GACvB,yBAAyB,GACzB,0BAA0B,CAAC;AAE/B,MAAM,MAAM,uBAAuB,GAC/B,CAAC,QAAQ,CAAC,IAAI,CAAC,yBAAyB,EAAE,oBAAoB,CAAC,CAAC,GAC9D,QAAQ,CAAC;IAAE,kBAAkB,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC,GAC3C,QAAQ,CAAC,0BAA0B,CAAC,CAAC;AAgBzC,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,CA6BzE;AAED,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,eAAe,GAC1B,uBAAuB,CAgBzB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAI7E;AAED;;;;;GAKG;AACH,wBAAgB,sCAAsC,CACpD,UAAU,EAAE,uBAAuB,GAClC,MAAM,GAAG,SAAS,CAEpB;AAKD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;CAIe,CAAC;AAE9C,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;CAYd,CAAC"}
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AACpD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE9D,sGAAsG;AACtG,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,QAAQ,EAAE,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EACrD,IAAI,EAAE,mBAAmB;;;;;;;;EAiB1B;AAED,eAAO,MAAM,aAAa;;CAEsB,CAAC;AAEjD,KAAK,eAAe,GAAG,QAAQ,CAAC;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,QAAQ,CAAC;CAChB,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,MAAM,yBAAyB,GAAG,eAAe,GACrD,QAAQ,CAAC;IAAE,UAAU,CAAC,EAAE,IAAI,CAAC;IAAC,kBAAkB,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAE9D,MAAM,MAAM,0BAA0B,GAAG,eAAe,GACtD,QAAQ,CAAC;IAAE,UAAU,EAAE,KAAK,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE9D,MAAM,MAAM,eAAe,GACvB,yBAAyB,GACzB,0BAA0B,CAAC;AAE/B,MAAM,MAAM,uBAAuB,GAC/B,CAAC,QAAQ,CAAC,IAAI,CAAC,yBAAyB,EAAE,oBAAoB,CAAC,CAAC,GAC9D,QAAQ,CAAC;IAAE,kBAAkB,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC,GAC3C,QAAQ,CAAC,0BAA0B,CAAC,CAAC;AAgBzC,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,CA6BzE;AAED,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,eAAe,GAC1B,uBAAuB,CAgBzB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAI7E;AAED;;;;;GAKG;AACH,wBAAgB,sCAAsC,CACpD,UAAU,EAAE,uBAAuB,GAClC,MAAM,GAAG,SAAS,CAEpB;AAKD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;CAIe,CAAC;AAE9C,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;CAYd,CAAC"}
package/dist/image.js CHANGED
@@ -1,4 +1,23 @@
1
1
  import { semanticColors } from "./semantic-colors.js";
2
+ /** Exact image inspection sizes use layout units, not device physical pixels (Utilverse ADR-0020). */
3
+ export function resolveImageInspectionGeometry(image, viewport, mode) {
4
+ if (![image.width, image.height, viewport.width, viewport.height].every(value => Number.isFinite(value) && value > 0)) {
5
+ throw new RangeError("Image inspection needs positive finite image and viewport sizes");
6
+ }
7
+ if (mode !== "fit" && mode !== "double" && mode !== "pixels")
8
+ throw new TypeError("Unknown image inspection mode");
9
+ // Fit can enlarge a small result, as the existing product preview does. Clamping
10
+ // it to 1 would make fit and pixels indistinguishable for those images.
11
+ const scale = mode === "pixels" ? 1 : Math.min(viewport.width / image.width, viewport.height / image.height) * (mode === "double" ? 2 : 1);
12
+ const width = image.width * scale, height = image.height * scale;
13
+ if (![scale, width, height].every(value => Number.isFinite(value) && value > 0)) {
14
+ throw new RangeError("Image inspection geometry exceeds finite layout range");
15
+ }
16
+ return { scale, width, height, panBounds: {
17
+ x: Math.max(0, width - viewport.width) / 2,
18
+ y: Math.max(0, height - viewport.height) / 2,
19
+ } };
20
+ }
2
21
  export const imageDefaults = {
3
22
  fit: "cover",
4
23
  };
package/dist/image.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"image.js","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SemanticIconName } from \"./icon.js\";\nimport type { IconTone } from \"./component-recipes.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\nexport type ImageFit = \"cover\" | \"contain\" | \"fill\";\nexport type ImageLoadStatus = \"idle\" | \"loading\" | \"loaded\" | \"error\";\n\nexport const imageDefaults = {\n fit: \"cover\",\n} as const satisfies Readonly<{ fit: ImageFit }>;\n\ntype ImageAppearance = Readonly<{\n src: string;\n /** Intrinsic dimensions reserve layout space before the asset loads. */\n width: number;\n height: number;\n fit?: ImageFit;\n}>;\n\n/**\n * Same shape as Icon's decorative/informative split: a picture that only\n * repeats a caption or label already visible nearby stays decorative, and\n * only a self-sufficient picture requires localized alt copy.\n */\nexport type DecorativeImageDescriptor = ImageAppearance &\n Readonly<{ decorative?: true; accessibilityLabel?: never }>;\n\nexport type InformativeImageDescriptor = ImageAppearance &\n Readonly<{ decorative: false; accessibilityLabel: string }>;\n\nexport type ImageDescriptor =\n | DecorativeImageDescriptor\n | InformativeImageDescriptor;\n\nexport type ResolvedImageDescriptor =\n | (Required<Omit<DecorativeImageDescriptor, \"accessibilityLabel\">> &\n Readonly<{ accessibilityLabel?: never }>)\n | Required<InformativeImageDescriptor>;\n\nconst fits: readonly ImageFit[] = [\"cover\", \"contain\", \"fill\"];\n\nfunction assertCopy(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Image ${field} must not be empty`);\n }\n}\n\nfunction assertDimension(value: number, field: string): void {\n if (!Number.isFinite(value) || value <= 0) {\n throw new RangeError(`Image ${field} must be a positive finite number`);\n }\n}\n\nexport function validateImageDescriptor(descriptor: ImageDescriptor): void {\n assertCopy(descriptor.src, \"src\");\n assertDimension(descriptor.width, \"width\");\n assertDimension(descriptor.height, \"height\");\n if (descriptor.fit !== undefined && !fits.includes(descriptor.fit)) {\n throw new TypeError(`Unsupported Image fit: ${String(descriptor.fit)}`);\n }\n const runtimeDecorative = (\n descriptor as Readonly<{ decorative?: unknown }>\n ).decorative;\n const runtimeLabel = (\n descriptor as Readonly<{ accessibilityLabel?: unknown }>\n ).accessibilityLabel;\n if (\n runtimeDecorative !== undefined &&\n runtimeDecorative !== true &&\n runtimeDecorative !== false\n ) {\n throw new TypeError(\"Image decorative must be a boolean when provided\");\n }\n if (runtimeDecorative === false) {\n if (typeof runtimeLabel !== \"string\" || runtimeLabel.trim().length === 0) {\n throw new TypeError(\n \"Informative Image accessibilityLabel must not be empty\",\n );\n }\n } else if (runtimeLabel !== undefined) {\n throw new TypeError(\"Decorative Image must not provide accessibilityLabel\");\n }\n}\n\nexport function resolveImageDescriptor(\n descriptor: ImageDescriptor,\n): ResolvedImageDescriptor {\n validateImageDescriptor(descriptor);\n const common = {\n src: descriptor.src,\n width: descriptor.width,\n height: descriptor.height,\n fit: descriptor.fit ?? imageDefaults.fit,\n } as const;\n if (descriptor.decorative === false) {\n return {\n ...common,\n decorative: false,\n accessibilityLabel: descriptor.accessibilityLabel,\n } as ResolvedImageDescriptor;\n }\n return { ...common, decorative: true } as ResolvedImageDescriptor;\n}\n\n/**\n * Reserves layout space ahead of load so a slow asset never shifts\n * surrounding content — the same problem `width`/`height` solve for\n * `next/image`, translated without a bundler dependency.\n */\nexport function resolveImageAspectRatio(width: number, height: number): number {\n assertDimension(width, \"width\");\n assertDimension(height, \"height\");\n return width / height;\n}\n\n/**\n * The fallback node on `error` always carries the resolved\n * `accessibilityLabel`, never a generic \"broken image\" string. An\n * informative picture's meaning does not disappear because the asset\n * failed to load; only its visual form changes.\n */\nexport function resolveImageFallbackAccessibilityLabel(\n descriptor: ResolvedImageDescriptor,\n): string | undefined {\n return descriptor.decorative ? undefined : descriptor.accessibilityLabel;\n}\n\nconst errorIconName: SemanticIconName = \"error\";\nconst fallbackIconTone: IconTone = \"secondary\";\n\n/**\n * Web `object-fit` and RN `resizeMode` diverge only in the `fill` case;\n * `fills` is the platform-neutral name and `nativeResizeModes` is the one\n * translation renderers need, kept here instead of re-derived per product.\n */\nexport const nativeResizeModes = {\n cover: \"cover\",\n contain: \"contain\",\n fill: \"stretch\",\n} as const satisfies Record<ImageFit, string>;\n\nexport const imageRecipe = {\n slots: [\"root\", \"image\", \"placeholder\", \"fallbackIcon\"] as const,\n defaults: imageDefaults,\n fits,\n placeholder: {\n background: semanticColors.canvas,\n },\n fallback: {\n background: semanticColors.canvas,\n icon: { name: errorIconName, tone: fallbackIconTone },\n },\n radius: \"md\",\n} as const;\n"]}
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SemanticIconName } from \"./icon.js\";\nimport type { IconTone } from \"./component-recipes.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\nexport type ImageFit = \"cover\" | \"contain\" | \"fill\";\nexport type ImageLoadStatus = \"idle\" | \"loading\" | \"loaded\" | \"error\";\n\nexport type ImageInspectionMode = \"fit\" | \"double\" | \"pixels\";\n\n/** Exact image inspection sizes use layout units, not device physical pixels (Utilverse ADR-0020). */\nexport function resolveImageInspectionGeometry(\n image: Readonly<{ width: number; height: number }>,\n viewport: Readonly<{ width: number; height: number }>,\n mode: ImageInspectionMode,\n) {\n if (![image.width, image.height, viewport.width, viewport.height].every(value => Number.isFinite(value) && value > 0)) {\n throw new RangeError(\"Image inspection needs positive finite image and viewport sizes\");\n }\n if (mode !== \"fit\" && mode !== \"double\" && mode !== \"pixels\") throw new TypeError(\"Unknown image inspection mode\");\n // Fit can enlarge a small result, as the existing product preview does. Clamping\n // it to 1 would make fit and pixels indistinguishable for those images.\n const scale = mode === \"pixels\" ? 1 : Math.min(viewport.width / image.width, viewport.height / image.height) * (mode === \"double\" ? 2 : 1);\n const width = image.width * scale, height = image.height * scale;\n if (![scale, width, height].every(value => Number.isFinite(value) && value > 0)) {\n throw new RangeError(\"Image inspection geometry exceeds finite layout range\");\n }\n return { scale, width, height, panBounds: {\n x: Math.max(0, width - viewport.width) / 2,\n y: Math.max(0, height - viewport.height) / 2,\n } };\n}\n\nexport const imageDefaults = {\n fit: \"cover\",\n} as const satisfies Readonly<{ fit: ImageFit }>;\n\ntype ImageAppearance = Readonly<{\n src: string;\n /** Intrinsic dimensions reserve layout space before the asset loads. */\n width: number;\n height: number;\n fit?: ImageFit;\n}>;\n\n/**\n * Same shape as Icon's decorative/informative split: a picture that only\n * repeats a caption or label already visible nearby stays decorative, and\n * only a self-sufficient picture requires localized alt copy.\n */\nexport type DecorativeImageDescriptor = ImageAppearance &\n Readonly<{ decorative?: true; accessibilityLabel?: never }>;\n\nexport type InformativeImageDescriptor = ImageAppearance &\n Readonly<{ decorative: false; accessibilityLabel: string }>;\n\nexport type ImageDescriptor =\n | DecorativeImageDescriptor\n | InformativeImageDescriptor;\n\nexport type ResolvedImageDescriptor =\n | (Required<Omit<DecorativeImageDescriptor, \"accessibilityLabel\">> &\n Readonly<{ accessibilityLabel?: never }>)\n | Required<InformativeImageDescriptor>;\n\nconst fits: readonly ImageFit[] = [\"cover\", \"contain\", \"fill\"];\n\nfunction assertCopy(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Image ${field} must not be empty`);\n }\n}\n\nfunction assertDimension(value: number, field: string): void {\n if (!Number.isFinite(value) || value <= 0) {\n throw new RangeError(`Image ${field} must be a positive finite number`);\n }\n}\n\nexport function validateImageDescriptor(descriptor: ImageDescriptor): void {\n assertCopy(descriptor.src, \"src\");\n assertDimension(descriptor.width, \"width\");\n assertDimension(descriptor.height, \"height\");\n if (descriptor.fit !== undefined && !fits.includes(descriptor.fit)) {\n throw new TypeError(`Unsupported Image fit: ${String(descriptor.fit)}`);\n }\n const runtimeDecorative = (\n descriptor as Readonly<{ decorative?: unknown }>\n ).decorative;\n const runtimeLabel = (\n descriptor as Readonly<{ accessibilityLabel?: unknown }>\n ).accessibilityLabel;\n if (\n runtimeDecorative !== undefined &&\n runtimeDecorative !== true &&\n runtimeDecorative !== false\n ) {\n throw new TypeError(\"Image decorative must be a boolean when provided\");\n }\n if (runtimeDecorative === false) {\n if (typeof runtimeLabel !== \"string\" || runtimeLabel.trim().length === 0) {\n throw new TypeError(\n \"Informative Image accessibilityLabel must not be empty\",\n );\n }\n } else if (runtimeLabel !== undefined) {\n throw new TypeError(\"Decorative Image must not provide accessibilityLabel\");\n }\n}\n\nexport function resolveImageDescriptor(\n descriptor: ImageDescriptor,\n): ResolvedImageDescriptor {\n validateImageDescriptor(descriptor);\n const common = {\n src: descriptor.src,\n width: descriptor.width,\n height: descriptor.height,\n fit: descriptor.fit ?? imageDefaults.fit,\n } as const;\n if (descriptor.decorative === false) {\n return {\n ...common,\n decorative: false,\n accessibilityLabel: descriptor.accessibilityLabel,\n } as ResolvedImageDescriptor;\n }\n return { ...common, decorative: true } as ResolvedImageDescriptor;\n}\n\n/**\n * Reserves layout space ahead of load so a slow asset never shifts\n * surrounding content — the same problem `width`/`height` solve for\n * `next/image`, translated without a bundler dependency.\n */\nexport function resolveImageAspectRatio(width: number, height: number): number {\n assertDimension(width, \"width\");\n assertDimension(height, \"height\");\n return width / height;\n}\n\n/**\n * The fallback node on `error` always carries the resolved\n * `accessibilityLabel`, never a generic \"broken image\" string. An\n * informative picture's meaning does not disappear because the asset\n * failed to load; only its visual form changes.\n */\nexport function resolveImageFallbackAccessibilityLabel(\n descriptor: ResolvedImageDescriptor,\n): string | undefined {\n return descriptor.decorative ? undefined : descriptor.accessibilityLabel;\n}\n\nconst errorIconName: SemanticIconName = \"error\";\nconst fallbackIconTone: IconTone = \"secondary\";\n\n/**\n * Web `object-fit` and RN `resizeMode` diverge only in the `fill` case;\n * `fills` is the platform-neutral name and `nativeResizeModes` is the one\n * translation renderers need, kept here instead of re-derived per product.\n */\nexport const nativeResizeModes = {\n cover: \"cover\",\n contain: \"contain\",\n fill: \"stretch\",\n} as const satisfies Record<ImageFit, string>;\n\nexport const imageRecipe = {\n slots: [\"root\", \"image\", \"placeholder\", \"fallbackIcon\"] as const,\n defaults: imageDefaults,\n fits,\n placeholder: {\n background: semanticColors.canvas,\n },\n fallback: {\n background: semanticColors.canvas,\n icon: { name: errorIconName, tone: fallbackIconTone },\n },\n radius: \"md\",\n} as const;\n"]}
@@ -0,0 +1,2 @@
1
+ export declare const effectNoiseTile = "data:image/png;base64,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";
2
+ //# sourceMappingURL=effect-noise.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effect-noise.d.ts","sourceRoot":"","sources":["../../src/internal/effect-noise.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe,+5NAA+5N,CAAC"}
@@ -0,0 +1,4 @@
1
+ // Generated by scripts/generate-effect-noise.py. Native FeTurbulence is unimplemented;
2
+ // a shared static mask avoids a new runtime and keeps noise separate from dotted grain.
3
+ export const effectNoiseTile = "data:image/png;base64,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";
4
+ //# sourceMappingURL=effect-noise.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effect-noise.js","sourceRoot":"","sources":["../../src/internal/effect-noise.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,wFAAwF;AACxF,MAAM,CAAC,MAAM,eAAe,GAAG,45NAA45N,CAAC","sourcesContent":["// Generated by scripts/generate-effect-noise.py. Native FeTurbulence is unimplemented;\n// a shared static mask avoids a new runtime and keeps noise separate from dotted grain.\nexport const effectNoiseTile = \"data:image/png;base64,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\";\n"]}
@@ -0,0 +1,32 @@
1
+ import { type ScrollMetrics } from './scroll-progress.js';
2
+ export type ProgressiveBlurDescriptor = Readonly<{
3
+ edge: 'top' | 'bottom' | 'start' | 'end';
4
+ /** Physical extent in logical pixels; choose after measuring the content host. */
5
+ extent: number;
6
+ /** Host-relative blur strength, not a cross-platform pixel-equivalence claim. */
7
+ strength: number;
8
+ layers?: number;
9
+ content: {
10
+ kind: 'decoration';
11
+ } | {
12
+ kind: 'scroll';
13
+ metrics: ScrollMetrics;
14
+ focused: boolean;
15
+ };
16
+ }>;
17
+ export type ProgressiveBlurLayer = Readonly<{
18
+ strength: number;
19
+ start: number;
20
+ end: number;
21
+ }>;
22
+ export declare function resolveProgressiveBlur(descriptor: ProgressiveBlurDescriptor, direction?: 'ltr' | 'rtl'): {
23
+ side: "top" | "bottom" | "left" | "right";
24
+ extent: number;
25
+ visible: boolean;
26
+ layers: Readonly<{
27
+ strength: number;
28
+ start: number;
29
+ end: number;
30
+ }>[];
31
+ };
32
+ //# sourceMappingURL=progressive-blur.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progressive-blur.d.ts","sourceRoot":"","sources":["../src/progressive-blur.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC9E,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;IACzC,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE;QAAE,IAAI,EAAE,YAAY,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;CAChG,CAAC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC9F,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,yBAAyB,EAAE,SAAS,GAAE,KAAK,GAAG,KAAa;;;;;kBADtD,MAAM;eAAS,MAAM;aAAO,MAAM;;EAqBzF"}
@@ -0,0 +1,28 @@
1
+ import { resolveScrollEdges } from './scroll-progress.js';
2
+ export function resolveProgressiveBlur(descriptor, direction = 'ltr') {
3
+ const { edge, extent, strength, content } = descriptor;
4
+ if (!['top', 'bottom', 'start', 'end'].includes(edge))
5
+ throw new TypeError('Unknown blur edge');
6
+ if (!Number.isFinite(extent) || extent <= 0 || extent > 160)
7
+ throw new RangeError('Blur extent must be between 0 and 160');
8
+ if (!Number.isFinite(strength) || strength < 0 || strength > 1)
9
+ throw new RangeError('Blur strength must be between 0 and 1');
10
+ // Four layers are an experimental bounded starting point, not a measured
11
+ // performance promise. Unlike copying the upstream eight layers, callers can
12
+ // compare 2–8 on their real host before enabling the decoration.
13
+ const count = descriptor.layers ?? 4;
14
+ if (!Number.isInteger(count) || count < 2 || count > 8)
15
+ throw new RangeError('Blur layers must be an integer between 2 and 8');
16
+ if (content.kind !== 'scroll' && content.kind !== 'decoration')
17
+ throw new TypeError('Unknown blur content kind');
18
+ const side = edge === 'start' ? (direction === 'rtl' ? 'right' : 'left') : edge === 'end' ? (direction === 'rtl' ? 'left' : 'right') : edge;
19
+ let visible = strength > 0;
20
+ if (content.kind === 'scroll') {
21
+ const edges = resolveScrollEdges(content.metrics);
22
+ visible &&= !content.focused && (edge === 'top' || edge === 'start' ? edges.before : edges.after);
23
+ }
24
+ return { side, extent, visible, layers: Array.from({ length: count }, (_, index) => ({
25
+ strength: strength * (index + 1) / count, start: index / count, end: (index + 1) / count,
26
+ })) };
27
+ }
28
+ //# sourceMappingURL=progressive-blur.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progressive-blur.js","sourceRoot":"","sources":["../src/progressive-blur.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAsB,MAAM,sBAAsB,CAAC;AAW9E,MAAM,UAAU,sBAAsB,CAAC,UAAqC,EAAE,YAA2B,KAAK;IAC5G,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC;IACvD,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAChG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,IAAI,MAAM,GAAG,GAAG;QAAE,MAAM,IAAI,UAAU,CAAC,uCAAuC,CAAC,CAAC;IAC3H,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,IAAI,QAAQ,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,uCAAuC,CAAC,CAAC;IAC9H,yEAAyE;IACzE,6EAA6E;IAC7E,iEAAiE;IACjE,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,gDAAgD,CAAC,CAAC;IAC/H,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY;QAAE,MAAM,IAAI,SAAS,CAAC,2BAA2B,CAAC,CAAC;IACjH,MAAM,IAAI,GAAwC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACjL,IAAI,OAAO,GAAG,QAAQ,GAAG,CAAC,CAAC;IAC3B,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAClD,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACpG,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAwB,EAAE,CAAC,CAAC;YACzG,QAAQ,EAAE,QAAQ,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,KAAK;SACzF,CAAC,CAAC,EAAE,CAAC;AACR,CAAC","sourcesContent":["import { resolveScrollEdges, type ScrollMetrics } from './scroll-progress.js';\nexport type ProgressiveBlurDescriptor = Readonly<{\n edge: 'top' | 'bottom' | 'start' | 'end';\n /** Physical extent in logical pixels; choose after measuring the content host. */\n extent: number;\n /** Host-relative blur strength, not a cross-platform pixel-equivalence claim. */\n strength: number;\n layers?: number;\n content: { kind: 'decoration' } | { kind: 'scroll'; metrics: ScrollMetrics; focused: boolean };\n}>;\nexport type ProgressiveBlurLayer = Readonly<{ strength: number; start: number; end: number }>;\nexport function resolveProgressiveBlur(descriptor: ProgressiveBlurDescriptor, direction: 'ltr' | 'rtl' = 'ltr') {\n const { edge, extent, strength, content } = descriptor;\n if (!['top', 'bottom', 'start', 'end'].includes(edge)) throw new TypeError('Unknown blur edge');\n if (!Number.isFinite(extent) || extent <= 0 || extent > 160) throw new RangeError('Blur extent must be between 0 and 160');\n if (!Number.isFinite(strength) || strength < 0 || strength > 1) throw new RangeError('Blur strength must be between 0 and 1');\n // Four layers are an experimental bounded starting point, not a measured\n // performance promise. Unlike copying the upstream eight layers, callers can\n // compare 2–8 on their real host before enabling the decoration.\n const count = descriptor.layers ?? 4;\n if (!Number.isInteger(count) || count < 2 || count > 8) throw new RangeError('Blur layers must be an integer between 2 and 8');\n if (content.kind !== 'scroll' && content.kind !== 'decoration') throw new TypeError('Unknown blur content kind');\n const side: 'top' | 'bottom' | 'left' | 'right' = edge === 'start' ? (direction === 'rtl' ? 'right' : 'left') : edge === 'end' ? (direction === 'rtl' ? 'left' : 'right') : edge;\n let visible = strength > 0;\n if (content.kind === 'scroll') {\n const edges = resolveScrollEdges(content.metrics);\n visible &&= !content.focused && (edge === 'top' || edge === 'start' ? edges.before : edges.after);\n }\n return { side, extent, visible, layers: Array.from({ length: count }, (_, index): ProgressiveBlurLayer => ({\n strength: strength * (index + 1) / count, start: index / count, end: (index + 1) / count,\n })) };\n}\n"]}