@hjmds/design-contracts 1.13.0 → 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 (126) 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 +5 -0
  7. package/dist/catalog.d.ts.map +1 -1
  8. package/dist/component-recipes.d.ts +14 -0
  9. package/dist/component-recipes.d.ts.map +1 -1
  10. package/dist/component-recipes.js +16 -1
  11. package/dist/component-recipes.js.map +1 -1
  12. package/dist/content-transition.d.ts +17 -0
  13. package/dist/content-transition.d.ts.map +1 -1
  14. package/dist/content-transition.js +21 -0
  15. package/dist/content-transition.js.map +1 -1
  16. package/dist/date-entry.d.ts +71 -0
  17. package/dist/date-entry.d.ts.map +1 -0
  18. package/dist/date-entry.js +78 -0
  19. package/dist/date-entry.js.map +1 -0
  20. package/dist/document-resource.d.ts +104 -0
  21. package/dist/document-resource.d.ts.map +1 -0
  22. package/dist/document-resource.js +77 -0
  23. package/dist/document-resource.js.map +1 -0
  24. package/dist/effect-surface.d.ts +6 -1
  25. package/dist/effect-surface.d.ts.map +1 -1
  26. package/dist/effect-surface.js +5 -2
  27. package/dist/effect-surface.js.map +1 -1
  28. package/dist/field-group.d.ts +47 -0
  29. package/dist/field-group.d.ts.map +1 -0
  30. package/dist/field-group.js +92 -0
  31. package/dist/field-group.js.map +1 -0
  32. package/dist/image.d.ts +17 -0
  33. package/dist/image.d.ts.map +1 -1
  34. package/dist/image.js +19 -0
  35. package/dist/image.js.map +1 -1
  36. package/dist/internal/effect-noise.d.ts +2 -0
  37. package/dist/internal/effect-noise.d.ts.map +1 -0
  38. package/dist/internal/effect-noise.js +4 -0
  39. package/dist/internal/effect-noise.js.map +1 -0
  40. package/dist/progressive-blur.d.ts +32 -0
  41. package/dist/progressive-blur.d.ts.map +1 -0
  42. package/dist/progressive-blur.js +28 -0
  43. package/dist/progressive-blur.js.map +1 -0
  44. package/dist/reference-controls.d.ts +32 -0
  45. package/dist/reference-controls.d.ts.map +1 -0
  46. package/dist/reference-controls.js +28 -0
  47. package/dist/reference-controls.js.map +1 -0
  48. package/dist/scroll-progress.d.ts +7 -1
  49. package/dist/scroll-progress.d.ts.map +1 -1
  50. package/dist/scroll-progress.js +21 -2
  51. package/dist/scroll-progress.js.map +1 -1
  52. package/dist/text-annotation.d.ts +43 -0
  53. package/dist/text-annotation.d.ts.map +1 -0
  54. package/dist/text-annotation.js +137 -0
  55. package/dist/text-annotation.js.map +1 -0
  56. package/dist/version.d.ts +1 -1
  57. package/dist/version.js +1 -1
  58. package/dist/version.js.map +1 -1
  59. package/docs/agreement.md +14 -0
  60. package/docs/dialog.md +16 -1
  61. package/docs/effect-surface.md +19 -3
  62. package/docs/generated/component-maturity.md +1 -1
  63. package/docs/generated/renderer-evidence.json +3 -3
  64. package/docs/generated/renderer-evidence.md +1 -1
  65. package/docs/generated/showcase-manifest.json +1 -1
  66. package/docs/image.md +17 -0
  67. package/docs/optional-adapters.md +25 -0
  68. package/docs/popover.md +15 -0
  69. package/docs/rating.md +6 -1
  70. package/docs/reference-controls.md +40 -0
  71. package/docs/screen-patterns.md +2 -0
  72. package/docs/sheet.md +9 -0
  73. package/docs/task-list.md +15 -1
  74. package/docs/text-annotation.md +85 -0
  75. package/docs/toggle-group.md +6 -0
  76. package/docs/usage/README.md +18 -1
  77. package/docs/usage/components/agreement.md +13 -3
  78. package/docs/usage/components/alert-dialog.md +7 -1
  79. package/docs/usage/components/avatar.md +33 -1
  80. package/docs/usage/components/card.md +7 -1
  81. package/docs/usage/components/carousel.md +5 -1
  82. package/docs/usage/components/chat-message.md +11 -1
  83. package/docs/usage/components/chip.md +10 -2
  84. package/docs/usage/components/content-transition.md +28 -4
  85. package/docs/usage/components/dialog.md +18 -1
  86. package/docs/usage/components/effect-surface.md +4 -2
  87. package/docs/usage/components/empty-state.md +10 -2
  88. package/docs/usage/components/field.md +23 -0
  89. package/docs/usage/components/form.md +6 -1
  90. package/docs/usage/components/image-comparison.md +82 -0
  91. package/docs/usage/components/image.md +81 -2
  92. package/docs/usage/components/keyboard-avoiding.md +6 -1
  93. package/docs/usage/components/link.md +3 -1
  94. package/docs/usage/components/list-row.md +3 -1
  95. package/docs/usage/components/list.md +6 -1
  96. package/docs/usage/components/message-composer.md +5 -0
  97. package/docs/usage/components/popover.md +7 -1
  98. package/docs/usage/components/progress.md +28 -0
  99. package/docs/usage/components/progressive-blur.md +113 -0
  100. package/docs/usage/components/rating.md +74 -0
  101. package/docs/usage/components/search-field.md +10 -0
  102. package/docs/usage/components/search-screen.md +16 -5
  103. package/docs/usage/components/segmented-control.md +29 -4
  104. package/docs/usage/components/sheet.md +7 -1
  105. package/docs/usage/components/statistic.md +11 -0
  106. package/docs/usage/components/tags-input.md +5 -0
  107. package/docs/usage/components/toast.md +5 -0
  108. package/docs/usage/components/upload-item.md +3 -1
  109. package/docs/usage/compositions/action-feedback.md +77 -0
  110. package/docs/usage/compositions/adaptive-content.md +81 -0
  111. package/docs/usage/compositions/context-toolbar.md +85 -0
  112. package/docs/usage/compositions/date-entry.md +108 -0
  113. package/docs/usage/compositions/document-resource.md +124 -0
  114. package/docs/usage/compositions/field-group.md +104 -0
  115. package/docs/usage/compositions/illustrated-outcome.md +91 -0
  116. package/docs/usage/compositions/live-list.md +104 -0
  117. package/docs/usage/compositions/optional-adapters.md +1 -1
  118. package/docs/usage/compositions/origin-dialog.md +108 -0
  119. package/docs/usage/compositions/selection-motion.md +73 -0
  120. package/docs/usage/compositions/texture-comparison.md +86 -0
  121. package/docs/usage/compositions/upload-recovery.md +80 -0
  122. package/docs/usage/compositions/video-dialog.md +100 -0
  123. package/docs/usage/screens/common-search.md +7 -2
  124. package/docs/usage/screens/flow-onboarding.md +5 -3
  125. package/docs/usage/screens/product-bento.md +117 -0
  126. package/package.json +37 -1
@@ -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":"AA4BA,SAAS,WAAW,CAAC,KAAc,EAAE,IAAY;IAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,IAAI,SAAS,CAAC,eAAe,IAAI,wBAAwB,CAAC,CAAC;AACnH,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,2BAA2B,CAAC,MAA2C;IACrF,IAAI,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IACtF,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7D,OAAO;QACL,MAAM,CAAC,IAAyC;YAC9C,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;YACrD,8EAA8E;YAC9E,6EAA6E;YAC7E,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,IAAI,EAAE;gBAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;oBAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC1E,KAAK,MAAM,EAAE,IAAI,OAAO;gBAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;oBAAE,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YACpE,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QAClF,CAAC;QACD,yFAAyF;QACzF,OAAO,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC9B,KAAK,CAAyB,EAAU,EAAE,QAAiC;YACzE,0EAA0E;YAC1E,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC/B,OAAO,CAAC,GAAG,IAAI,EAAE,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAChG,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,KAAc,EAAE,IAAY;IAChD,IAAI,KAAK,KAAK,SAAS;QAAE,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACrC,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;AACrH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,UAAgC;IAOhE,WAAW,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACvC,YAAY,CAAC,UAAU,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IACpD,eAAe,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACrC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,qCAAqC,CAAC,CAAC;IAClG,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;QACtC,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QAClC,WAAW,CAAC,KAAK,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QACxC,YAAY,CAAC,KAAK,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;QACrD,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QACzC,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAChC,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;QACnF,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACpB,CAAC;IACD,IAAI,UAAU,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACnC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC;QAC3C,0EAA0E;QAC1E,8EAA8E;QAC9E,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YAChH,MAAM,IAAI,SAAS,CAAC,6DAA6D,CAAC,CAAC;QACrF,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,IAAI,KAAK,CAAC;IAC9C,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,KAAK,EAAE,UAAU,CAAC,KAAK;QACvB,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO;QAChC,QAAQ;QACR,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;YAClD,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;YACxG,MAAM,OAAO,GAAkD,EAAE,CAAC;YAClE,IAAI,UAAU,CAAC,WAAW,KAAK,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;YAC9H,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;YACpH,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YAClG,IAAI,UAAU,KAAK,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;YAChG,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,CAAC,KAAK;gBACnF,+EAA+E;gBAC/E,QAAQ,EAAE,QAAQ,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,UAAU,KAAK,SAAS;gBAC/G,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC9E,CAAC,CAAC,CAAC;KACJ,CAAC,CAAC;AACL,CAAC","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":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAKtD,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,GAAG,EAAE,OAAO;CACkC,CAAC;AA8BjD,MAAM,IAAI,GAAwB,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AAE/D,SAAS,UAAU,CAAC,KAAa,EAAE,KAAa;IAC9C,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,SAAS,CAAC,SAAS,KAAK,oBAAoB,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,KAAa,EAAE,KAAa;IACnD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,UAAU,CAAC,SAAS,KAAK,mCAAmC,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,UAA2B;IACjE,UAAU,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAClC,eAAe,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC3C,eAAe,CAAC,UAAU,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC7C,IAAI,UAAU,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,SAAS,CAAC,0BAA0B,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,iBAAiB,GACrB,UACD,CAAC,UAAU,CAAC;IACb,MAAM,YAAY,GAChB,UACD,CAAC,kBAAkB,CAAC;IACrB,IACE,iBAAiB,KAAK,SAAS;QAC/B,iBAAiB,KAAK,IAAI;QAC1B,iBAAiB,KAAK,KAAK,EAC3B,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,kDAAkD,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,iBAAiB,KAAK,KAAK,EAAE,CAAC;QAChC,IAAI,OAAO,YAAY,KAAK,QAAQ,IAAI,YAAY,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzE,MAAM,IAAI,SAAS,CACjB,wDAAwD,CACzD,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QACtC,MAAM,IAAI,SAAS,CAAC,sDAAsD,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,UAA2B;IAE3B,uBAAuB,CAAC,UAAU,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG;QACb,GAAG,EAAE,UAAU,CAAC,GAAG;QACnB,KAAK,EAAE,UAAU,CAAC,KAAK;QACvB,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,GAAG,EAAE,UAAU,CAAC,GAAG,IAAI,aAAa,CAAC,GAAG;KAChC,CAAC;IACX,IAAI,UAAU,CAAC,UAAU,KAAK,KAAK,EAAE,CAAC;QACpC,OAAO;YACL,GAAG,MAAM;YACT,UAAU,EAAE,KAAK;YACjB,kBAAkB,EAAE,UAAU,CAAC,kBAAkB;SACvB,CAAC;IAC/B,CAAC;IACD,OAAO,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,IAAI,EAA6B,CAAC;AACpE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAa,EAAE,MAAc;IACnE,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAChC,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAClC,OAAO,KAAK,GAAG,MAAM,CAAC;AACxB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sCAAsC,CACpD,UAAmC;IAEnC,OAAO,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,kBAAkB,CAAC;AAC3E,CAAC;AAED,MAAM,aAAa,GAAqB,OAAO,CAAC;AAChD,MAAM,gBAAgB,GAAa,WAAW,CAAC;AAE/C;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;CAC4B,CAAC;AAE9C,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,CAAU;IAChE,QAAQ,EAAE,aAAa;IACvB,IAAI;IACJ,WAAW,EAAE;QACX,UAAU,EAAE,cAAc,CAAC,MAAM;KAClC;IACD,QAAQ,EAAE;QACR,UAAU,EAAE,cAAc,CAAC,MAAM;QACjC,IAAI,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,gBAAgB,EAAE;KACtD;IACD,MAAM,EAAE,IAAI;CACJ,CAAC","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"]}
@@ -0,0 +1,32 @@
1
+ export type RatingDescriptor = Readonly<{
2
+ label: string;
3
+ value: number | null;
4
+ max?: number;
5
+ readOnly?: boolean;
6
+ }>;
7
+ /** A bounded row remains a rating rather than an unbounded repeated-icon list.
8
+ * Null means unrated; zero and fractional averages are meaningful only in read-only summaries. */
9
+ export declare function resolveRating({ label, value, max, readOnly }: RatingDescriptor): {
10
+ max: number;
11
+ value: number | null;
12
+ fractions: number[];
13
+ };
14
+ export type ComparisonImage = Readonly<{
15
+ src: string;
16
+ width: number;
17
+ height: number;
18
+ label: string;
19
+ }>;
20
+ export type ImageComparisonDescriptor = Readonly<{
21
+ label: string;
22
+ before: ComparisonImage;
23
+ after: ComparisonImage;
24
+ value: number;
25
+ }>;
26
+ /** Both pictures share one coordinate system. Reject mismatched aspect ratios
27
+ * instead of silently stretching or cropping the evidence being compared. */
28
+ export declare function resolveImageComparison({ label, before, after, value }: ImageComparisonDescriptor): {
29
+ aspectRatio: number;
30
+ fraction: number;
31
+ };
32
+ //# sourceMappingURL=reference-controls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference-controls.d.ts","sourceRoot":"","sources":["../src/reference-controls.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACnH;kGACkG;AAClG,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAO,EAAE,QAAgB,EAAE,EAAE,gBAAgB;;;;EAO1F;AACD,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AACtG,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AACpI;6EAC6E;AAC7E,wBAAgB,sBAAsB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,yBAAyB;;;EAOhG"}
@@ -0,0 +1,28 @@
1
+ import { validateImageDescriptor } from "./image.js";
2
+ /** A bounded row remains a rating rather than an unbounded repeated-icon list.
3
+ * Null means unrated; zero and fractional averages are meaningful only in read-only summaries. */
4
+ export function resolveRating({ label, value, max = 5, readOnly = false }) {
5
+ if (!label.trim())
6
+ throw new TypeError("Rating requires a label");
7
+ if (!Number.isInteger(max) || max < 1 || max > 10)
8
+ throw new RangeError("Rating max must be an integer from 1 to 10");
9
+ if (value !== null && (!Number.isFinite(value) || value < 0 || value > max || (!readOnly && (!Number.isInteger(value) || value === 0)))) {
10
+ throw new RangeError("Interactive ratings require whole scores from 1 to max, or null");
11
+ }
12
+ return { max, value, fractions: Array.from({ length: max }, (_, index) => Math.max(0, Math.min(1, (value ?? 0) - index))) };
13
+ }
14
+ /** Both pictures share one coordinate system. Reject mismatched aspect ratios
15
+ * instead of silently stretching or cropping the evidence being compared. */
16
+ export function resolveImageComparison({ label, before, after, value }) {
17
+ if (!label.trim())
18
+ throw new TypeError("ImageComparison requires a label");
19
+ for (const image of [before, after])
20
+ validateImageDescriptor({ ...image, decorative: false, accessibilityLabel: image.label });
21
+ if (!Number.isFinite(value) || value < 0 || value > 100)
22
+ throw new RangeError("ImageComparison value must be from 0 to 100");
23
+ const aspectRatio = before.width / before.height;
24
+ if (Math.abs(aspectRatio - after.width / after.height) > 0.000001)
25
+ throw new RangeError("Compared images require the same aspect ratio");
26
+ return { aspectRatio, fraction: value / 100 };
27
+ }
28
+ //# sourceMappingURL=reference-controls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference-controls.js","sourceRoot":"","sources":["../src/reference-controls.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAGrD;kGACkG;AAClG,MAAM,UAAU,aAAa,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAoB;IACzF,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAC;IAClE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,EAAE;QAAE,MAAM,IAAI,UAAU,CAAC,4CAA4C,CAAC,CAAC;IACtH,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,GAAG,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACxI,MAAM,IAAI,UAAU,CAAC,iEAAiE,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9H,CAAC;AAGD;6EAC6E;AAC7E,MAAM,UAAU,sBAAsB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAA6B;IAC/F,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,IAAI,SAAS,CAAC,kCAAkC,CAAC,CAAC;IAC3E,KAAK,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;QAAE,uBAAuB,CAAC,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/H,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,GAAG;QAAE,MAAM,IAAI,UAAU,CAAC,6CAA6C,CAAC,CAAC;IAC7H,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;IACjD,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,QAAQ;QAAE,MAAM,IAAI,UAAU,CAAC,+CAA+C,CAAC,CAAC;IACzI,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,GAAG,GAAG,EAAE,CAAC;AAChD,CAAC","sourcesContent":["import { validateImageDescriptor } from \"./image.js\";\n\nexport type RatingDescriptor = Readonly<{ label: string; value: number | null; max?: number; readOnly?: boolean }>;\n/** A bounded row remains a rating rather than an unbounded repeated-icon list.\n * Null means unrated; zero and fractional averages are meaningful only in read-only summaries. */\nexport function resolveRating({ label, value, max = 5, readOnly = false }: RatingDescriptor) {\n if (!label.trim()) throw new TypeError(\"Rating requires a label\");\n if (!Number.isInteger(max) || max < 1 || max > 10) throw new RangeError(\"Rating max must be an integer from 1 to 10\");\n if (value !== null && (!Number.isFinite(value) || value < 0 || value > max || (!readOnly && (!Number.isInteger(value) || value === 0)))) {\n throw new RangeError(\"Interactive ratings require whole scores from 1 to max, or null\");\n }\n return { max, value, fractions: Array.from({ length: max }, (_, index) => Math.max(0, Math.min(1, (value ?? 0) - index))) };\n}\nexport type ComparisonImage = Readonly<{ src: string; width: number; height: number; label: string }>;\nexport type ImageComparisonDescriptor = Readonly<{ label: string; before: ComparisonImage; after: ComparisonImage; value: number }>;\n/** Both pictures share one coordinate system. Reject mismatched aspect ratios\n * instead of silently stretching or cropping the evidence being compared. */\nexport function resolveImageComparison({ label, before, after, value }: ImageComparisonDescriptor) {\n if (!label.trim()) throw new TypeError(\"ImageComparison requires a label\");\n for (const image of [before, after]) validateImageDescriptor({ ...image, decorative: false, accessibilityLabel: image.label });\n if (!Number.isFinite(value) || value < 0 || value > 100) throw new RangeError(\"ImageComparison value must be from 0 to 100\");\n const aspectRatio = before.width / before.height;\n if (Math.abs(aspectRatio - after.width / after.height) > 0.000001) throw new RangeError(\"Compared images require the same aspect ratio\");\n return { aspectRatio, fraction: value / 100 };\n}\n"]}
@@ -4,5 +4,11 @@ export type ScrollMetrics = Readonly<{
4
4
  contentSize: number;
5
5
  viewportSize: number;
6
6
  }>;
7
- export declare function resolveScrollProgress({ offset, contentSize, viewportSize }: ScrollMetrics): number;
7
+ export declare function resolveScrollProgress(metrics: ScrollMetrics): number;
8
+ export type ScrollEdges = Readonly<{
9
+ before: boolean;
10
+ after: boolean;
11
+ }>;
12
+ /** Whether more content exists in either logical direction; not a scroll action. */
13
+ export declare function resolveScrollEdges(metrics: ScrollMetrics): ScrollEdges;
8
14
  //# sourceMappingURL=scroll-progress.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scroll-progress.d.ts","sourceRoot":"","sources":["../src/scroll-progress.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AACpG,wBAAgB,qBAAqB,CAAC,EAAC,MAAM,EAAC,WAAW,EAAC,YAAY,EAAC,EAAC,aAAa,GAAE,MAAM,CAM5F"}
1
+ {"version":3,"file":"scroll-progress.d.ts","sourceRoot":"","sources":["../src/scroll-progress.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAMpG,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAOpE;AAED,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACxE,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,WAAW,CAWtE"}
@@ -1,10 +1,29 @@
1
- export function resolveScrollProgress({ offset, contentSize, viewportSize }) {
2
- if (![offset, contentSize, viewportSize].every(Number.isFinite) || contentSize < 0 || viewportSize < 0)
1
+ function validateScrollMetrics({ offset, contentSize, viewportSize }) {
2
+ if (![offset, contentSize, viewportSize].every(Number.isFinite) || contentSize < 0 || viewportSize < 0) {
3
3
  throw new RangeError('Scroll metrics must be finite with nonnegative sizes');
4
+ }
5
+ }
6
+ export function resolveScrollProgress(metrics) {
7
+ validateScrollMetrics(metrics);
8
+ const { offset, contentSize, viewportSize } = metrics;
4
9
  // Unmeasured hosts are not complete; content that fits a measured viewport is.
5
10
  if (viewportSize === 0)
6
11
  return 0;
7
12
  const extent = contentSize - viewportSize;
8
13
  return extent <= 0 ? 1 : Math.min(1, Math.max(0, offset / extent));
9
14
  }
15
+ /** Whether more content exists in either logical direction; not a scroll action. */
16
+ export function resolveScrollEdges(metrics) {
17
+ validateScrollMetrics(metrics);
18
+ const { offset, contentSize, viewportSize } = metrics;
19
+ // The reference blur obscured the last row even at the end. One logical pixel
20
+ // tolerates integer content/client dimensions paired with fractional offsets;
21
+ // exact equality would leave that edge covered. See progressive-blur adoption.
22
+ const tolerance = 1;
23
+ const extent = contentSize - viewportSize;
24
+ if (viewportSize === 0 || extent <= tolerance)
25
+ return { before: false, after: false };
26
+ const boundedOffset = Math.min(extent, Math.max(0, offset));
27
+ return { before: boundedOffset > tolerance, after: extent - boundedOffset > tolerance };
28
+ }
10
29
  //# sourceMappingURL=scroll-progress.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scroll-progress.js","sourceRoot":"","sources":["../src/scroll-progress.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,qBAAqB,CAAC,EAAC,MAAM,EAAC,WAAW,EAAC,YAAY,EAAe;IACnF,IAAI,CAAC,CAAC,MAAM,EAAC,WAAW,EAAC,YAAY,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,WAAW,GAAG,CAAC,IAAI,YAAY,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,sDAAsD,CAAC,CAAC;IACnL,+EAA+E;IAC/E,IAAI,YAAY,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACjC,MAAM,MAAM,GAAC,WAAW,GAAC,YAAY,CAAC;IACtC,OAAO,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAC,MAAM,GAAC,MAAM,CAAC,CAAC,CAAC;AACjE,CAAC","sourcesContent":["/** Logical forward offset. Horizontal RTL hosts normalize their own scrollLeft. */\nexport type ScrollMetrics = Readonly<{ offset: number; contentSize: number; viewportSize: number }>;\nexport function resolveScrollProgress({offset,contentSize,viewportSize}:ScrollMetrics):number {\n if (![offset,contentSize,viewportSize].every(Number.isFinite) || contentSize < 0 || viewportSize < 0) throw new RangeError('Scroll metrics must be finite with nonnegative sizes');\n // Unmeasured hosts are not complete; content that fits a measured viewport is.\n if (viewportSize === 0) return 0;\n const extent=contentSize-viewportSize;\n return extent <= 0 ? 1 : Math.min(1,Math.max(0,offset/extent));\n}\n"]}
1
+ {"version":3,"file":"scroll-progress.js","sourceRoot":"","sources":["../src/scroll-progress.ts"],"names":[],"mappings":"AAEA,SAAS,qBAAqB,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAiB;IACjF,IAAI,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,WAAW,GAAG,CAAC,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;QACvG,MAAM,IAAI,UAAU,CAAC,sDAAsD,CAAC,CAAC;IAC/E,CAAC;AACH,CAAC;AACD,MAAM,UAAU,qBAAqB,CAAC,OAAsB;IAC1D,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;IACtD,+EAA+E;IAC/E,IAAI,YAAY,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,WAAW,GAAG,YAAY,CAAC;IAC1C,OAAO,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;AACrE,CAAC;AAGD,oFAAoF;AACpF,MAAM,UAAU,kBAAkB,CAAC,OAAsB;IACvD,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;IACtD,8EAA8E;IAC9E,8EAA8E;IAC9E,+EAA+E;IAC/E,MAAM,SAAS,GAAG,CAAC,CAAC;IACpB,MAAM,MAAM,GAAG,WAAW,GAAG,YAAY,CAAC;IAC1C,IAAI,YAAY,KAAK,CAAC,IAAI,MAAM,IAAI,SAAS;QAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACtF,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5D,OAAO,EAAE,MAAM,EAAE,aAAa,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,EAAE,CAAC;AAC1F,CAAC","sourcesContent":["/** Logical forward offset. Horizontal RTL hosts normalize their own scrollLeft. */\nexport type ScrollMetrics = Readonly<{ offset: number; contentSize: number; viewportSize: number }>;\nfunction validateScrollMetrics({ offset, contentSize, viewportSize }: ScrollMetrics): void {\n if (![offset, contentSize, viewportSize].every(Number.isFinite) || contentSize < 0 || viewportSize < 0) {\n throw new RangeError('Scroll metrics must be finite with nonnegative sizes');\n }\n}\nexport function resolveScrollProgress(metrics: ScrollMetrics): number {\n validateScrollMetrics(metrics);\n const { offset, contentSize, viewportSize } = metrics;\n // Unmeasured hosts are not complete; content that fits a measured viewport is.\n if (viewportSize === 0) return 0;\n const extent = contentSize - viewportSize;\n return extent <= 0 ? 1 : Math.min(1, Math.max(0, offset / extent));\n}\n\nexport type ScrollEdges = Readonly<{ before: boolean; after: boolean }>;\n/** Whether more content exists in either logical direction; not a scroll action. */\nexport function resolveScrollEdges(metrics: ScrollMetrics): ScrollEdges {\n validateScrollMetrics(metrics);\n const { offset, contentSize, viewportSize } = metrics;\n // The reference blur obscured the last row even at the end. One logical pixel\n // tolerates integer content/client dimensions paired with fractional offsets;\n // exact equality would leave that edge covered. See progressive-blur adoption.\n const tolerance = 1;\n const extent = contentSize - viewportSize;\n if (viewportSize === 0 || extent <= tolerance) return { before: false, after: false };\n const boundedOffset = Math.min(extent, Math.max(0, offset));\n return { before: boundedOffset > tolerance, after: extent - boundedOffset > tolerance };\n}\n"]}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Experimental drawing geometry, not a public TextFormat variant. A renderer
3
+ * supplies measured visual line rectangles; estimating widths from character
4
+ * counts would break proportional fonts, Korean wrapping, and bidirectional text.
5
+ * See docs/text-annotation.md for the unfinished renderer boundary.
6
+ */
7
+ export declare const textAnnotationActions: readonly ["highlight", "underline", "box", "circle", "strike-through", "crossed-off", "bracket"];
8
+ export type TextAnnotationAction = typeof textAnnotationActions[number];
9
+ export type TextAnnotationRect = Readonly<{
10
+ x: number;
11
+ y: number;
12
+ width: number;
13
+ height: number;
14
+ }>;
15
+ export type TextAnnotationFragment = TextAnnotationRect & Readonly<{
16
+ lineIndex: number;
17
+ }>;
18
+ export type TextAnnotationPath = Readonly<{
19
+ d: string;
20
+ /** Filled marker strokes belong behind the text; outlines are unfilled. */
21
+ paint: 'fill' | 'stroke';
22
+ strokeWidth: number;
23
+ lineIndex: number;
24
+ }>;
25
+ export type TextAnnotationGeometry = Readonly<{
26
+ paths: readonly TextAnnotationPath[];
27
+ bounds: TextAnnotationRect | null;
28
+ }>;
29
+ /** Merge adjacent font/bidi runs only within their measured visual line. A gap
30
+ * in a partial selection stays unpainted even when both runs share a line.
31
+ * Hosts supply line identity from the same layout engine that paints the text.
32
+ */
33
+ export declare function mergeTextAnnotationFragments(fragments: readonly TextAnnotationFragment[]): TextAnnotationRect[];
34
+ /**
35
+ * Small deterministic offsets give the two passes a hand-drawn character.
36
+ * Random-per-render geometry was rejected: unrelated state updates must not
37
+ * make the annotation jump or create server/client output differences.
38
+ */
39
+ export declare function resolveTextAnnotationGeometry(lines: readonly TextAnnotationRect[], action: TextAnnotationAction, options?: Readonly<{
40
+ strokeWidth?: number;
41
+ padding?: number;
42
+ }>): TextAnnotationGeometry;
43
+ //# sourceMappingURL=text-annotation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-annotation.d.ts","sourceRoot":"","sources":["../src/text-annotation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,kGAExB,CAAC;AACX,MAAM,MAAM,oBAAoB,GAAG,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;AACxE,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AACnG,MAAM,MAAM,sBAAsB,GAAG,kBAAkB,GAAG,QAAQ,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC1F,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,CAAC,EAAE,MAAM,CAAC;IACV,2EAA2E;IAC3E,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACnC,CAAC,CAAC;AAMH;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,SAAS,sBAAsB,EAAE,GAAG,kBAAkB,EAAE,CAqB/G;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,MAAM,EAAE,oBAAoB,EAC5B,OAAO,GAAE,QAAQ,CAAC;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAM,GACjE,sBAAsB,CAoExB"}