@printwithsynergy/artwork-pdf-editor 0.2.0 → 0.4.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.
- package/LICENSE +62 -0
- package/dist/components/AccessibilityHintsPanel.d.ts +142 -0
- package/dist/components/AccessibilityHintsPanel.d.ts.map +1 -0
- package/dist/components/AccessibilityHintsPanel.js +158 -0
- package/dist/components/AccessibilityHintsPanel.js.map +1 -0
- package/dist/components/AnnotationOverlay.d.ts +142 -0
- package/dist/components/AnnotationOverlay.d.ts.map +1 -0
- package/dist/components/AnnotationOverlay.js +141 -0
- package/dist/components/AnnotationOverlay.js.map +1 -0
- package/dist/components/AnnotationsSidebar.d.ts +98 -0
- package/dist/components/AnnotationsSidebar.d.ts.map +1 -0
- package/dist/components/AnnotationsSidebar.js +100 -0
- package/dist/components/AnnotationsSidebar.js.map +1 -0
- package/dist/components/BarcodeGeneratorPanel.d.ts +58 -0
- package/dist/components/BarcodeGeneratorPanel.d.ts.map +1 -0
- package/dist/components/BarcodeGeneratorPanel.js +91 -0
- package/dist/components/BarcodeGeneratorPanel.js.map +1 -0
- package/dist/components/BraillePanel.d.ts +99 -0
- package/dist/components/BraillePanel.d.ts.map +1 -0
- package/dist/components/BraillePanel.js +221 -0
- package/dist/components/BraillePanel.js.map +1 -0
- package/dist/components/BrandAssetsPanel.d.ts +130 -0
- package/dist/components/BrandAssetsPanel.d.ts.map +1 -0
- package/dist/components/BrandAssetsPanel.js +125 -0
- package/dist/components/BrandAssetsPanel.js.map +1 -0
- package/dist/components/BrandConsistencyPanel.d.ts +140 -0
- package/dist/components/BrandConsistencyPanel.d.ts.map +1 -0
- package/dist/components/BrandConsistencyPanel.js +158 -0
- package/dist/components/BrandConsistencyPanel.js.map +1 -0
- package/dist/components/ComplianceFindingsPanel.d.ts +62 -0
- package/dist/components/ComplianceFindingsPanel.d.ts.map +1 -0
- package/dist/components/ComplianceFindingsPanel.js +118 -0
- package/dist/components/ComplianceFindingsPanel.js.map +1 -0
- package/dist/components/DesignSuggestionsPanel.d.ts +148 -0
- package/dist/components/DesignSuggestionsPanel.d.ts.map +1 -0
- package/dist/components/DesignSuggestionsPanel.js +154 -0
- package/dist/components/DesignSuggestionsPanel.js.map +1 -0
- package/dist/components/DielineParametersPanel.d.ts +62 -0
- package/dist/components/DielineParametersPanel.d.ts.map +1 -0
- package/dist/components/DielineParametersPanel.js +170 -0
- package/dist/components/DielineParametersPanel.js.map +1 -0
- package/dist/components/DielinePreview.d.ts +150 -0
- package/dist/components/DielinePreview.d.ts.map +1 -0
- package/dist/components/DielinePreview.js +146 -0
- package/dist/components/DielinePreview.js.map +1 -0
- package/dist/components/EditorApp.d.ts +18 -3
- package/dist/components/EditorApp.d.ts.map +1 -1
- package/dist/components/EditorApp.js +42 -7
- package/dist/components/EditorApp.js.map +1 -1
- package/dist/components/EditorCanvas.d.ts +31 -2
- package/dist/components/EditorCanvas.d.ts.map +1 -1
- package/dist/components/EditorCanvas.js +97 -16
- package/dist/components/EditorCanvas.js.map +1 -1
- package/dist/components/EmailNotifyPanel.d.ts +165 -0
- package/dist/components/EmailNotifyPanel.d.ts.map +1 -0
- package/dist/components/EmailNotifyPanel.js +211 -0
- package/dist/components/EmailNotifyPanel.js.map +1 -0
- package/dist/components/FileDropZone.d.ts +9 -1
- package/dist/components/FileDropZone.d.ts.map +1 -1
- package/dist/components/FileDropZone.js +53 -5
- package/dist/components/FileDropZone.js.map +1 -1
- package/dist/components/FoldEditorPanel.d.ts +68 -0
- package/dist/components/FoldEditorPanel.d.ts.map +1 -0
- package/dist/components/FoldEditorPanel.js +65 -0
- package/dist/components/FoldEditorPanel.js.map +1 -0
- package/dist/components/FoldPreviewOverlay.d.ts +48 -0
- package/dist/components/FoldPreviewOverlay.d.ts.map +1 -0
- package/dist/components/FoldPreviewOverlay.js +182 -0
- package/dist/components/FoldPreviewOverlay.js.map +1 -0
- package/dist/components/Gs1DigitalLinkPanel.d.ts +103 -0
- package/dist/components/Gs1DigitalLinkPanel.d.ts.map +1 -0
- package/dist/components/Gs1DigitalLinkPanel.js +199 -0
- package/dist/components/Gs1DigitalLinkPanel.js.map +1 -0
- package/dist/components/HistoryPanel.d.ts +39 -0
- package/dist/components/HistoryPanel.d.ts.map +1 -0
- package/dist/components/HistoryPanel.js +72 -0
- package/dist/components/HistoryPanel.js.map +1 -0
- package/dist/components/IccSoftProofOverlay.d.ts +67 -0
- package/dist/components/IccSoftProofOverlay.d.ts.map +1 -0
- package/dist/components/IccSoftProofOverlay.js +119 -0
- package/dist/components/IccSoftProofOverlay.js.map +1 -0
- package/dist/components/ImposePanel.d.ts +71 -0
- package/dist/components/ImposePanel.d.ts.map +1 -0
- package/dist/components/ImposePanel.js +127 -0
- package/dist/components/ImposePanel.js.map +1 -0
- package/dist/components/InksPanel.d.ts +61 -0
- package/dist/components/InksPanel.d.ts.map +1 -0
- package/dist/components/InksPanel.js +84 -0
- package/dist/components/InksPanel.js.map +1 -0
- package/dist/components/JobSetupPanel.d.ts +118 -0
- package/dist/components/JobSetupPanel.d.ts.map +1 -0
- package/dist/components/JobSetupPanel.js +169 -0
- package/dist/components/JobSetupPanel.js.map +1 -0
- package/dist/components/LayersPanel.d.ts.map +1 -1
- package/dist/components/LayersPanel.js +1 -0
- package/dist/components/LayersPanel.js.map +1 -1
- package/dist/components/MarkLibraryPanel.d.ts +131 -0
- package/dist/components/MarkLibraryPanel.d.ts.map +1 -0
- package/dist/components/MarkLibraryPanel.js +184 -0
- package/dist/components/MarkLibraryPanel.js.map +1 -0
- package/dist/components/MisEstimateButton.d.ts +73 -0
- package/dist/components/MisEstimateButton.d.ts.map +1 -0
- package/dist/components/MisEstimateButton.js +57 -0
- package/dist/components/MisEstimateButton.js.map +1 -0
- package/dist/components/NutritionPanel.d.ts +118 -0
- package/dist/components/NutritionPanel.d.ts.map +1 -0
- package/dist/components/NutritionPanel.js +169 -0
- package/dist/components/NutritionPanel.js.map +1 -0
- package/dist/components/PageNavigator.d.ts.map +1 -1
- package/dist/components/PageNavigator.js +6 -1
- package/dist/components/PageNavigator.js.map +1 -1
- package/dist/components/PaletteManager.d.ts +32 -0
- package/dist/components/PaletteManager.d.ts.map +1 -0
- package/dist/components/PaletteManager.js +89 -0
- package/dist/components/PaletteManager.js.map +1 -0
- package/dist/components/PaletteToSpotPanel.d.ts +122 -0
- package/dist/components/PaletteToSpotPanel.d.ts.map +1 -0
- package/dist/components/PaletteToSpotPanel.js +160 -0
- package/dist/components/PaletteToSpotPanel.js.map +1 -0
- package/dist/components/PreflightAutoFixPanel.d.ts +110 -0
- package/dist/components/PreflightAutoFixPanel.d.ts.map +1 -0
- package/dist/components/PreflightAutoFixPanel.js +119 -0
- package/dist/components/PreflightAutoFixPanel.js.map +1 -0
- package/dist/components/PreflightDiffPanel.d.ts +127 -0
- package/dist/components/PreflightDiffPanel.d.ts.map +1 -0
- package/dist/components/PreflightDiffPanel.js +0 -0
- package/dist/components/PreflightDiffPanel.js.map +1 -0
- package/dist/components/ProcessRulesPanel.d.ts +81 -0
- package/dist/components/ProcessRulesPanel.d.ts.map +1 -0
- package/dist/components/ProcessRulesPanel.js +143 -0
- package/dist/components/ProcessRulesPanel.js.map +1 -0
- package/dist/components/SlackNotifyPanel.d.ts +139 -0
- package/dist/components/SlackNotifyPanel.d.ts.map +1 -0
- package/dist/components/SlackNotifyPanel.js +133 -0
- package/dist/components/SlackNotifyPanel.js.map +1 -0
- package/dist/components/SmartSpotMatchPanel.d.ts +143 -0
- package/dist/components/SmartSpotMatchPanel.d.ts.map +1 -0
- package/dist/components/SmartSpotMatchPanel.js +159 -0
- package/dist/components/SmartSpotMatchPanel.js.map +1 -0
- package/dist/components/StreamingRenderProgress.d.ts +60 -0
- package/dist/components/StreamingRenderProgress.d.ts.map +1 -0
- package/dist/components/StreamingRenderProgress.js +99 -0
- package/dist/components/StreamingRenderProgress.js.map +1 -0
- package/dist/components/SwatchesPicker.d.ts +83 -0
- package/dist/components/SwatchesPicker.d.ts.map +1 -0
- package/dist/components/SwatchesPicker.js +151 -0
- package/dist/components/SwatchesPicker.js.map +1 -0
- package/dist/components/TacOverlay.d.ts +47 -0
- package/dist/components/TacOverlay.d.ts.map +1 -0
- package/dist/components/TacOverlay.js +116 -0
- package/dist/components/TacOverlay.js.map +1 -0
- package/dist/components/TrapEditorPanel.d.ts +52 -0
- package/dist/components/TrapEditorPanel.d.ts.map +1 -0
- package/dist/components/TrapEditorPanel.js +64 -0
- package/dist/components/TrapEditorPanel.js.map +1 -0
- package/dist/components/TrapPreviewOverlay.d.ts +64 -0
- package/dist/components/TrapPreviewOverlay.d.ts.map +1 -0
- package/dist/components/TrapPreviewOverlay.js +120 -0
- package/dist/components/TrapPreviewOverlay.js.map +1 -0
- package/dist/components/VariantMatrixPanel.d.ts +61 -0
- package/dist/components/VariantMatrixPanel.d.ts.map +1 -0
- package/dist/components/VariantMatrixPanel.js +97 -0
- package/dist/components/VariantMatrixPanel.js.map +1 -0
- package/dist/components/VariantMatrixVersionPanel.d.ts +122 -0
- package/dist/components/VariantMatrixVersionPanel.d.ts.map +1 -0
- package/dist/components/VariantMatrixVersionPanel.js +162 -0
- package/dist/components/VariantMatrixVersionPanel.js.map +1 -0
- package/dist/components/WebhookNotifyPanel.d.ts +160 -0
- package/dist/components/WebhookNotifyPanel.d.ts.map +1 -0
- package/dist/components/WebhookNotifyPanel.js +100 -0
- package/dist/components/WebhookNotifyPanel.js.map +1 -0
- package/dist/components/WhiteUnderbasePanel.d.ts +107 -0
- package/dist/components/WhiteUnderbasePanel.d.ts.map +1 -0
- package/dist/components/WhiteUnderbasePanel.js +104 -0
- package/dist/components/WhiteUnderbasePanel.js.map +1 -0
- package/dist/hooks/useEditorMode.d.ts +25 -5
- package/dist/hooks/useEditorMode.d.ts.map +1 -1
- package/dist/hooks/useEditorMode.js +18 -5
- package/dist/hooks/useEditorMode.js.map +1 -1
- package/dist/index.d.ts +51 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +51 -2
- package/dist/index.js.map +1 -1
- package/dist/lens/preflight-findings.d.ts.map +1 -1
- package/dist/lens/preflight-findings.js.map +1 -1
- package/dist/lib/barcode-scan.d.ts +154 -0
- package/dist/lib/barcode-scan.d.ts.map +1 -0
- package/dist/lib/barcode-scan.js +152 -0
- package/dist/lib/barcode-scan.js.map +1 -0
- package/dist/lib/color-math.d.ts +76 -0
- package/dist/lib/color-math.d.ts.map +1 -0
- package/dist/lib/color-math.js +96 -0
- package/dist/lib/color-math.js.map +1 -0
- package/dist/lib/dieline-template.d.ts +87 -0
- package/dist/lib/dieline-template.d.ts.map +1 -1
- package/dist/lib/dieline-template.js +163 -0
- package/dist/lib/dieline-template.js.map +1 -1
- package/dist/lib/editor-config.d.ts +390 -0
- package/dist/lib/editor-config.d.ts.map +1 -1
- package/dist/lib/editor-config.js +90 -0
- package/dist/lib/editor-config.js.map +1 -1
- package/dist/lib/fold-geometry.d.ts +144 -0
- package/dist/lib/fold-geometry.d.ts.map +1 -0
- package/dist/lib/fold-geometry.js +138 -0
- package/dist/lib/fold-geometry.js.map +1 -0
- package/dist/lib/merge-tokens.d.ts +81 -0
- package/dist/lib/merge-tokens.d.ts.map +1 -0
- package/dist/lib/merge-tokens.js +88 -0
- package/dist/lib/merge-tokens.js.map +1 -0
- package/dist/lib/palette-registry.d.ts +40 -0
- package/dist/lib/palette-registry.d.ts.map +1 -0
- package/dist/lib/palette-registry.js +50 -0
- package/dist/lib/palette-registry.js.map +1 -0
- package/dist/lib/panel-anchor.d.ts +101 -0
- package/dist/lib/panel-anchor.d.ts.map +1 -0
- package/dist/lib/panel-anchor.js +68 -0
- package/dist/lib/panel-anchor.js.map +1 -0
- package/dist/lib/preflight/checks.d.ts.map +1 -1
- package/dist/lib/preflight/checks.js +71 -0
- package/dist/lib/preflight/checks.js.map +1 -1
- package/dist/lib/preflight/types.d.ts.map +1 -1
- package/dist/lib/preflight/types.js +11 -0
- package/dist/lib/preflight/types.js.map +1 -1
- package/dist/lib/rasterize.d.ts +93 -0
- package/dist/lib/rasterize.d.ts.map +1 -0
- package/dist/lib/rasterize.js +117 -0
- package/dist/lib/rasterize.js.map +1 -0
- package/dist/lib/separations-registry.d.ts +99 -0
- package/dist/lib/separations-registry.d.ts.map +1 -0
- package/dist/lib/separations-registry.js +59 -0
- package/dist/lib/separations-registry.js.map +1 -0
- package/dist/lib/streaming-render.d.ts +100 -0
- package/dist/lib/streaming-render.d.ts.map +1 -0
- package/dist/lib/streaming-render.js +132 -0
- package/dist/lib/streaming-render.js.map +1 -0
- package/dist/lib/unwired.d.ts +29 -0
- package/dist/lib/unwired.d.ts.map +1 -0
- package/dist/lib/unwired.js +58 -0
- package/dist/lib/unwired.js.map +1 -0
- package/package.json +20 -11
- package/dist/components/SeparationsPanel.d.ts +0 -9
- package/dist/components/SeparationsPanel.d.ts.map +0 -1
- package/dist/components/SeparationsPanel.js +0 -168
- package/dist/components/SeparationsPanel.js.map +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
artworkPDF — WYSIWYG label & packaging artwork editor
|
|
2
|
+
Copyright (C) 2024-2026 Think Neverland LLC
|
|
3
|
+
|
|
4
|
+
This program is free software: you can redistribute it and/or modify
|
|
5
|
+
it under the terms of the GNU Affero General Public License as
|
|
6
|
+
published by the Free Software Foundation, either version 3 of the
|
|
7
|
+
License, or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
This program is distributed in the hope that it will be useful,
|
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
GNU Affero General Public License for more details.
|
|
13
|
+
|
|
14
|
+
You should have received a copy of the GNU Affero General Public
|
|
15
|
+
License along with this program. If not, see
|
|
16
|
+
<https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
GNU AFFERO GENERAL PUBLIC LICENSE
|
|
21
|
+
Version 3, 19 November 2007
|
|
22
|
+
|
|
23
|
+
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
24
|
+
Everyone is permitted to copy and distribute verbatim copies
|
|
25
|
+
of this license document, but changing it is not allowed.
|
|
26
|
+
|
|
27
|
+
Preamble
|
|
28
|
+
|
|
29
|
+
The GNU Affero General Public License is a free, copyleft license for
|
|
30
|
+
software and other kinds of works, specifically designed to ensure
|
|
31
|
+
cooperation with the community in the case of network server software.
|
|
32
|
+
|
|
33
|
+
The licenses for most software and other practical works are designed
|
|
34
|
+
to take away your freedom to share and change the works. By contrast,
|
|
35
|
+
our General Public Licenses are intended to guarantee your freedom to
|
|
36
|
+
share and change all versions of a program--to make sure it remains free
|
|
37
|
+
software for all its users.
|
|
38
|
+
|
|
39
|
+
When we speak of free software, we are referring to freedom, not
|
|
40
|
+
price. Our General Public Licenses are designed to make sure that you
|
|
41
|
+
have the freedom to distribute copies of free software (and charge for
|
|
42
|
+
them if you wish), to help you ensure that you receive source code or can
|
|
43
|
+
get it if you want it, to help you ensure that you can give other people
|
|
44
|
+
a copy of it, and let you know you can do these things.
|
|
45
|
+
|
|
46
|
+
Developers that use our General Public Licenses protect your rights
|
|
47
|
+
with two steps: (1) assert copyright on the software, and (2) offer
|
|
48
|
+
you this License which gives you legal permission to copy, distribute
|
|
49
|
+
and/or modify the software.
|
|
50
|
+
|
|
51
|
+
A secondary benefit of defending all users' freedom is that
|
|
52
|
+
improvements made in individual component, which are widely deployed on
|
|
53
|
+
network servers, become available to improve those servers too. Whether
|
|
54
|
+
this happens depends on what the server software does with the
|
|
55
|
+
modifications made in it. The GNU Affero General Public License
|
|
56
|
+
requires that modified source code be made available to the users.
|
|
57
|
+
In the case of a network server, this is relatively easy to achieve.
|
|
58
|
+
|
|
59
|
+
The precise terms and conditions for copying, distribution and
|
|
60
|
+
modification follow.
|
|
61
|
+
|
|
62
|
+
See <https://www.gnu.org/licenses/agpl-3.0.txt> for the full license text.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave 4 AI5 — Accessibility hints panel.
|
|
3
|
+
*
|
|
4
|
+
* Fourth panel in the Wave 4 AI family alongside AI1
|
|
5
|
+
* {@link DesignSuggestionsPanel} (proactive design hints), AI2
|
|
6
|
+
* {@link SmartSpotMatchPanel} (ΔE-ranked PANTONE matcher), and AI3
|
|
7
|
+
* {@link PreflightAutoFixPanel} (preflight remediation). AI5 surfaces
|
|
8
|
+
* accessibility findings (low contrast, missing alt text, text-size
|
|
9
|
+
* minimums, color-only signalling) sourced from a host-supplied
|
|
10
|
+
* loader. Rules-engine, ML adapter, or a tenant-deployed lint-pdf
|
|
11
|
+
* accessibility profile all fit the same loader contract.
|
|
12
|
+
*
|
|
13
|
+
* Findings carry an optional `objectId` so the host can scroll the
|
|
14
|
+
* offending canvas object into view, and an optional `severity` for
|
|
15
|
+
* triage. The same three-tier severity vocabulary as B2
|
|
16
|
+
* BrandConsistencyPanel — kept identical on purpose so a single host
|
|
17
|
+
* triage UI can render both panels' rows the same way.
|
|
18
|
+
*
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
import type { ReactElement } from "react";
|
|
22
|
+
/**
|
|
23
|
+
* Severity of an accessibility finding. Mirrors B2's vocabulary
|
|
24
|
+
* verbatim so hosts that surface both panels in one triage list get
|
|
25
|
+
* consistent ordering and colors.
|
|
26
|
+
*
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
29
|
+
export type AccessibilitySeverity = "error" | "warn" | "info";
|
|
30
|
+
/**
|
|
31
|
+
* Category an accessibility finding belongs to. Hosts can render
|
|
32
|
+
* category chips for quick scanning; the panel groups findings by
|
|
33
|
+
* severity, not category.
|
|
34
|
+
*
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
export type AccessibilityCategory = "contrast" | "alt-text" | "text-size" | "color-only" | "structure" | "other";
|
|
38
|
+
/**
|
|
39
|
+
* One accessibility finding. The optional `recommendation` is a
|
|
40
|
+
* single short remediation hint surfaced as a secondary line in the
|
|
41
|
+
* row; the optional `objectId` lets the host wire a focus / scroll
|
|
42
|
+
* affordance to the offending canvas object.
|
|
43
|
+
*
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
export type AccessibilityFinding = {
|
|
47
|
+
/** Stable identifier — the panel uses it as the React key and
|
|
48
|
+
* exposes it through `onSelect`. */
|
|
49
|
+
id: string;
|
|
50
|
+
category: AccessibilityCategory;
|
|
51
|
+
severity: AccessibilitySeverity;
|
|
52
|
+
summary: string;
|
|
53
|
+
/** Optional short remediation hint (one line). */
|
|
54
|
+
recommendation?: string;
|
|
55
|
+
/** Optional id of the canvas object the finding applies to. */
|
|
56
|
+
objectId?: string;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Host adapter — resolves the full accessibility finding list for the
|
|
60
|
+
* active document. Rejects on transport / validation errors and the
|
|
61
|
+
* panel surfaces the message inline. Hosts should memoize the
|
|
62
|
+
* function with `useCallback` so an unrelated parent re-render doesn't
|
|
63
|
+
* trigger a spurious refetch.
|
|
64
|
+
*
|
|
65
|
+
* @public
|
|
66
|
+
*/
|
|
67
|
+
export type AccessibilityHintsLoaderFn = () => Promise<readonly AccessibilityFinding[]>;
|
|
68
|
+
/**
|
|
69
|
+
* Filter spec accepted by {@link filterAccessibilityFindings}.
|
|
70
|
+
*
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
73
|
+
export type AccessibilityFilter = {
|
|
74
|
+
/** Pre-filter to a single severity. Absent → all severities. */
|
|
75
|
+
severity?: AccessibilitySeverity;
|
|
76
|
+
/** Pre-filter to a single category. Absent → all categories. */
|
|
77
|
+
category?: AccessibilityCategory;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Result row from {@link groupAccessibilityFindingsBySeverity}.
|
|
81
|
+
*
|
|
82
|
+
* @public
|
|
83
|
+
*/
|
|
84
|
+
export type AccessibilityGroup = {
|
|
85
|
+
severity: AccessibilitySeverity;
|
|
86
|
+
findings: readonly AccessibilityFinding[];
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Canonical severity order — errors first so press-blocking issues
|
|
90
|
+
* surface at the top. Identical to B2's order on purpose.
|
|
91
|
+
*
|
|
92
|
+
* @public
|
|
93
|
+
*/
|
|
94
|
+
export declare const ACCESSIBILITY_SEVERITY_ORDER: readonly AccessibilitySeverity[];
|
|
95
|
+
/**
|
|
96
|
+
* Pure helper — filters findings by severity / category. Returns a
|
|
97
|
+
* new array; preserves input order. Pure function.
|
|
98
|
+
*
|
|
99
|
+
* @public
|
|
100
|
+
*/
|
|
101
|
+
export declare function filterAccessibilityFindings(findings: readonly AccessibilityFinding[], filter: AccessibilityFilter): readonly AccessibilityFinding[];
|
|
102
|
+
/**
|
|
103
|
+
* Pure helper — groups findings by severity in
|
|
104
|
+
* {@link ACCESSIBILITY_SEVERITY_ORDER}. Returns a stable three-bucket
|
|
105
|
+
* shape so renderers iterate without absent-key checks.
|
|
106
|
+
*
|
|
107
|
+
* Pure function.
|
|
108
|
+
*
|
|
109
|
+
* @public
|
|
110
|
+
*/
|
|
111
|
+
export declare function groupAccessibilityFindingsBySeverity(findings: readonly AccessibilityFinding[]): readonly AccessibilityGroup[];
|
|
112
|
+
/**
|
|
113
|
+
* Configuration for the {@link AccessibilityHintsPanel}. The host
|
|
114
|
+
* always supplies the {@link AccessibilityHintsLoaderFn}; the
|
|
115
|
+
* optional `onSelect` callback lets the host wire a focus affordance
|
|
116
|
+
* (jump to the offending canvas object, focus the matching property
|
|
117
|
+
* editor row, etc.).
|
|
118
|
+
*
|
|
119
|
+
* @public
|
|
120
|
+
*/
|
|
121
|
+
export type AccessibilityHintsPanelProps = {
|
|
122
|
+
loader: AccessibilityHintsLoaderFn;
|
|
123
|
+
/** Optional severity pre-filter applied before grouping. */
|
|
124
|
+
filterSeverity?: AccessibilitySeverity;
|
|
125
|
+
/** Optional category pre-filter applied before grouping. */
|
|
126
|
+
filterCategory?: AccessibilityCategory;
|
|
127
|
+
/** Id of the currently active finding — the matching row renders
|
|
128
|
+
* in the "active" style. Hosts wire this to whichever surface
|
|
129
|
+
* drives selection. */
|
|
130
|
+
activeFindingId?: string;
|
|
131
|
+
/** Fired when the user clicks a row. */
|
|
132
|
+
onSelect?: (finding: AccessibilityFinding) => void;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Stateful panel — loads the finding stream on mount, surfaces the
|
|
136
|
+
* filtered + grouped list, and emits `onSelect` on row click.
|
|
137
|
+
* Handles loading / error / empty states inline.
|
|
138
|
+
*
|
|
139
|
+
* @public
|
|
140
|
+
*/
|
|
141
|
+
export declare function AccessibilityHintsPanel({ loader, filterSeverity, filterCategory, activeFindingId, onSelect, }: AccessibilityHintsPanelProps): ReactElement;
|
|
142
|
+
//# sourceMappingURL=AccessibilityHintsPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AccessibilityHintsPanel.d.ts","sourceRoot":"","sources":["../../src/components/AccessibilityHintsPanel.tsx"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C;;;;;;GAMG;AACH,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9D;;;;;;GAMG;AACH,MAAM,MAAM,qBAAqB,GAC7B,UAAU,GACV,UAAU,GACV,WAAW,GACX,YAAY,GACZ,WAAW,GACX,OAAO,CAAC;AAEZ;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC;yCACqC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,qBAAqB,CAAC;IAChC,QAAQ,EAAE,qBAAqB,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,0BAA0B,GAAG,MAAM,OAAO,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAAC;AAExF;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CAClC,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,qBAAqB,CAAC;IAChC,QAAQ,EAAE,SAAS,oBAAoB,EAAE,CAAC;CAC3C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,EAAE,SAAS,qBAAqB,EAIxE,CAAC;AAcF;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,SAAS,oBAAoB,EAAE,EACzC,MAAM,EAAE,mBAAmB,GAC1B,SAAS,oBAAoB,EAAE,CAMjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,SAAS,oBAAoB,EAAE,GACxC,SAAS,kBAAkB,EAAE,CAW/B;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,4BAA4B,GAAG;IACzC,MAAM,EAAE,0BAA0B,CAAC;IACnC,4DAA4D;IAC5D,cAAc,CAAC,EAAE,qBAAqB,CAAC;IACvC,4DAA4D;IAC5D,cAAc,CAAC,EAAE,qBAAqB,CAAC;IACvC;;4BAEwB;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACpD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,MAAM,EACN,cAAc,EACd,cAAc,EACd,eAAe,EACf,QAAQ,GACT,EAAE,4BAA4B,GAAG,YAAY,CA8G7C"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
"use client";
|
|
3
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
4
|
+
import { useEffect, useMemo, useState } from "react";
|
|
5
|
+
/**
|
|
6
|
+
* Canonical severity order — errors first so press-blocking issues
|
|
7
|
+
* surface at the top. Identical to B2's order on purpose.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export const ACCESSIBILITY_SEVERITY_ORDER = [
|
|
12
|
+
"error",
|
|
13
|
+
"warn",
|
|
14
|
+
"info",
|
|
15
|
+
];
|
|
16
|
+
const SEVERITY_LABELS = {
|
|
17
|
+
error: "Errors",
|
|
18
|
+
warn: "Warnings",
|
|
19
|
+
info: "Info",
|
|
20
|
+
};
|
|
21
|
+
const SEVERITY_COLORS = {
|
|
22
|
+
error: "#a00",
|
|
23
|
+
warn: "#a60",
|
|
24
|
+
info: "#06a",
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Pure helper — filters findings by severity / category. Returns a
|
|
28
|
+
* new array; preserves input order. Pure function.
|
|
29
|
+
*
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
export function filterAccessibilityFindings(findings, filter) {
|
|
33
|
+
return findings.filter((f) => {
|
|
34
|
+
if (filter.severity && f.severity !== filter.severity)
|
|
35
|
+
return false;
|
|
36
|
+
if (filter.category && f.category !== filter.category)
|
|
37
|
+
return false;
|
|
38
|
+
return true;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Pure helper — groups findings by severity in
|
|
43
|
+
* {@link ACCESSIBILITY_SEVERITY_ORDER}. Returns a stable three-bucket
|
|
44
|
+
* shape so renderers iterate without absent-key checks.
|
|
45
|
+
*
|
|
46
|
+
* Pure function.
|
|
47
|
+
*
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
50
|
+
export function groupAccessibilityFindingsBySeverity(findings) {
|
|
51
|
+
const buckets = new Map(ACCESSIBILITY_SEVERITY_ORDER.map((s) => [s, []]));
|
|
52
|
+
for (const f of findings) {
|
|
53
|
+
buckets.get(f.severity)?.push(f);
|
|
54
|
+
}
|
|
55
|
+
return ACCESSIBILITY_SEVERITY_ORDER.map((severity) => ({
|
|
56
|
+
severity,
|
|
57
|
+
findings: buckets.get(severity) ?? [],
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Stateful panel — loads the finding stream on mount, surfaces the
|
|
62
|
+
* filtered + grouped list, and emits `onSelect` on row click.
|
|
63
|
+
* Handles loading / error / empty states inline.
|
|
64
|
+
*
|
|
65
|
+
* @public
|
|
66
|
+
*/
|
|
67
|
+
export function AccessibilityHintsPanel({ loader, filterSeverity, filterCategory, activeFindingId, onSelect, }) {
|
|
68
|
+
const [findings, setFindings] = useState(null);
|
|
69
|
+
const [error, setError] = useState(null);
|
|
70
|
+
const [loading, setLoading] = useState(true);
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
let disposed = false;
|
|
73
|
+
setLoading(true);
|
|
74
|
+
setError(null);
|
|
75
|
+
setFindings(null);
|
|
76
|
+
void (async () => {
|
|
77
|
+
try {
|
|
78
|
+
const next = await loader();
|
|
79
|
+
if (disposed)
|
|
80
|
+
return;
|
|
81
|
+
setFindings(next);
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
if (disposed)
|
|
85
|
+
return;
|
|
86
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
if (!disposed)
|
|
90
|
+
setLoading(false);
|
|
91
|
+
}
|
|
92
|
+
})();
|
|
93
|
+
return () => {
|
|
94
|
+
disposed = true;
|
|
95
|
+
};
|
|
96
|
+
}, [loader]);
|
|
97
|
+
const visibleGroups = useMemo(() => {
|
|
98
|
+
if (!findings)
|
|
99
|
+
return null;
|
|
100
|
+
const filtered = filterAccessibilityFindings(findings, {
|
|
101
|
+
...(filterSeverity && { severity: filterSeverity }),
|
|
102
|
+
...(filterCategory && { category: filterCategory }),
|
|
103
|
+
});
|
|
104
|
+
return groupAccessibilityFindingsBySeverity(filtered);
|
|
105
|
+
}, [findings, filterSeverity, filterCategory]);
|
|
106
|
+
if (loading) {
|
|
107
|
+
return (_jsx("output", { "data-testid": "accessibility-hints-panel", "aria-live": "polite", style: { display: "block", padding: "0.5rem", opacity: 0.6 }, children: "Loading accessibility hints\u2026" }));
|
|
108
|
+
}
|
|
109
|
+
if (error) {
|
|
110
|
+
return (_jsxs("div", { "data-testid": "accessibility-hints-panel", role: "alert", style: { padding: "0.5rem", color: "#a00" }, children: ["Couldn't load accessibility hints: ", error] }));
|
|
111
|
+
}
|
|
112
|
+
if (!visibleGroups)
|
|
113
|
+
return _jsx("div", { "data-testid": "accessibility-hints-panel" });
|
|
114
|
+
const totalVisible = visibleGroups.reduce((s, g) => s + g.findings.length, 0);
|
|
115
|
+
return (_jsxs("div", { "data-testid": "accessibility-hints-panel", style: { padding: "0.5rem" }, children: [_jsx("header", { style: { marginBottom: "0.5rem" }, children: _jsxs("h3", { style: { margin: 0, fontSize: "0.875rem" }, children: ["Accessibility hints (", totalVisible, ")"] }) }), totalVisible === 0 && (_jsx("div", { "data-testid": "accessibility-hints-panel-empty", style: { opacity: 0.6, fontSize: "0.875rem" }, children: findings && findings.length > 0
|
|
116
|
+
? "No findings match the current filter."
|
|
117
|
+
: "No accessibility issues detected." })), visibleGroups.map((group) => {
|
|
118
|
+
if (group.findings.length === 0)
|
|
119
|
+
return null;
|
|
120
|
+
return (_jsxs("section", { "data-testid": `accessibility-hints-group-${group.severity}`, style: { marginBottom: "0.75rem" }, children: [_jsxs("h4", { style: {
|
|
121
|
+
margin: "0 0 0.25rem 0",
|
|
122
|
+
fontSize: "0.75rem",
|
|
123
|
+
color: SEVERITY_COLORS[group.severity],
|
|
124
|
+
textTransform: "uppercase",
|
|
125
|
+
letterSpacing: "0.05em",
|
|
126
|
+
}, children: [SEVERITY_LABELS[group.severity], " (", group.findings.length, ")"] }), _jsx("ul", { style: { listStyle: "none", padding: 0, margin: 0 }, children: group.findings.map((finding) => (_jsx(AccessibilityRow, { finding: finding, isActive: finding.id === activeFindingId, onSelect: onSelect }, finding.id))) })] }, group.severity));
|
|
127
|
+
})] }));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Renders one finding row. Intra-package helper — surface
|
|
131
|
+
* (category chip, summary, optional recommendation) is intentionally
|
|
132
|
+
* minimal so {@link AccessibilityHintsPanel} can swap it without
|
|
133
|
+
* downstream consumers depending on the shape.
|
|
134
|
+
*/
|
|
135
|
+
function AccessibilityRow({ finding, isActive, onSelect, }) {
|
|
136
|
+
const rowStyle = {
|
|
137
|
+
display: "block",
|
|
138
|
+
width: "100%",
|
|
139
|
+
padding: "0.375rem 0.5rem",
|
|
140
|
+
background: isActive ? "#e0ecff" : "transparent",
|
|
141
|
+
border: isActive ? "1px solid #2563eb" : "1px solid #ddd",
|
|
142
|
+
borderRadius: 4,
|
|
143
|
+
marginBottom: "0.25rem",
|
|
144
|
+
textAlign: "left",
|
|
145
|
+
cursor: onSelect ? "pointer" : "default",
|
|
146
|
+
};
|
|
147
|
+
const contents = (_jsxs(_Fragment, { children: [_jsxs("div", { style: { fontSize: "0.8125rem", fontWeight: 500 }, children: [_jsx("span", { style: {
|
|
148
|
+
display: "inline-block",
|
|
149
|
+
fontSize: "0.625rem",
|
|
150
|
+
padding: "0.0625rem 0.375rem",
|
|
151
|
+
borderRadius: 999,
|
|
152
|
+
background: "#eee",
|
|
153
|
+
color: "#595959",
|
|
154
|
+
marginRight: "0.375rem",
|
|
155
|
+
}, children: finding.category }), finding.summary] }), finding.recommendation && (_jsx("div", { style: { fontSize: "0.75rem", color: "#595959", marginTop: "0.125rem" }, children: finding.recommendation }))] }));
|
|
156
|
+
return (_jsx("li", { children: onSelect ? (_jsx("button", { type: "button", onClick: () => onSelect(finding), style: rowStyle, children: contents })) : (_jsx("div", { style: rowStyle, children: contents })) }));
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=AccessibilityHintsPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AccessibilityHintsPanel.js","sourceRoot":"","sources":["../../src/components/AccessibilityHintsPanel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave 4 X3 — Annotation overlay.
|
|
3
|
+
*
|
|
4
|
+
* Renders pinned author annotations from `PageV3.annotations` on top
|
|
5
|
+
* of the canvas. Three kinds, discriminated by `kind`:
|
|
6
|
+
* - `"point"` — anchor pin with optional note
|
|
7
|
+
* - `"area"` — bounded rectangle with optional note
|
|
8
|
+
* - `"text"` — anchor pin with a required text body
|
|
9
|
+
*
|
|
10
|
+
* Pure controlled SVG overlay. No state, no DOM events beyond an
|
|
11
|
+
* optional `onSelect` click — the host owns selection / hover /
|
|
12
|
+
* popover UI and writes back to `document.pages[i].annotations`.
|
|
13
|
+
*
|
|
14
|
+
* Pairs with the discriminated union in `@artworkpdf/document-model`'s
|
|
15
|
+
* `Annotation` type. The component duplicates the wire shape locally
|
|
16
|
+
* (same pattern as `BrandAsset` / `PreflightFinding`) so the editor
|
|
17
|
+
* stays consumable without the doc-model SDK.
|
|
18
|
+
*
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
import type { ReactElement } from "react";
|
|
22
|
+
/**
|
|
23
|
+
* Fields shared by every annotation variant. Mirrors document-model's
|
|
24
|
+
* internal `AnnotationBase` shape.
|
|
25
|
+
*
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
export type AnnotationBaseInput = {
|
|
29
|
+
id: string;
|
|
30
|
+
x: number;
|
|
31
|
+
y: number;
|
|
32
|
+
author?: string;
|
|
33
|
+
createdAt: string;
|
|
34
|
+
resolved?: boolean;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Pinpoint annotation — single x/y marker with an optional note.
|
|
38
|
+
* Width / height are forbidden by the union discrimination.
|
|
39
|
+
*
|
|
40
|
+
* @public
|
|
41
|
+
*/
|
|
42
|
+
export type PointAnnotationInput = AnnotationBaseInput & {
|
|
43
|
+
kind: "point";
|
|
44
|
+
text?: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Bounded-region annotation. `width` / `height` define a rectangle
|
|
48
|
+
* anchored at (`x`, `y`) in page coordinates. `text` is the optional
|
|
49
|
+
* note body.
|
|
50
|
+
*
|
|
51
|
+
* @public
|
|
52
|
+
*/
|
|
53
|
+
export type AreaAnnotationInput = AnnotationBaseInput & {
|
|
54
|
+
kind: "area";
|
|
55
|
+
width: number;
|
|
56
|
+
height: number;
|
|
57
|
+
text?: string;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Inline text annotation — anchor pin with a required text body.
|
|
61
|
+
* Use {@link PointAnnotationInput} when the note is optional.
|
|
62
|
+
*
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
65
|
+
export type TextAnnotationInput = AnnotationBaseInput & {
|
|
66
|
+
kind: "text";
|
|
67
|
+
text: string;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* One annotation, discriminated on `kind`. Structurally compatible
|
|
71
|
+
* with document-model's `Annotation` union.
|
|
72
|
+
*
|
|
73
|
+
* @public
|
|
74
|
+
*/
|
|
75
|
+
export type AnnotationOverlayAnnotation = PointAnnotationInput | AreaAnnotationInput | TextAnnotationInput;
|
|
76
|
+
/**
|
|
77
|
+
* @public
|
|
78
|
+
*/
|
|
79
|
+
export type AnnotationOverlayProps = {
|
|
80
|
+
/** Annotations to render. Typically `PageV3.annotations ?? []`. */
|
|
81
|
+
annotations: readonly AnnotationOverlayAnnotation[];
|
|
82
|
+
/** Width of the underlying page in CSS pixels. The SVG overlay
|
|
83
|
+
* matches the host's page-rendering surface so annotation anchors
|
|
84
|
+
* land where the user placed them. */
|
|
85
|
+
pageWidthPx: number;
|
|
86
|
+
/** Height of the underlying page in CSS pixels. */
|
|
87
|
+
pageHeightPx: number;
|
|
88
|
+
/** When `true`, also render annotations whose `resolved` flag is
|
|
89
|
+
* `true`. Absent / `false`: resolved annotations are hidden so
|
|
90
|
+
* the canvas stays focused on open conversations. */
|
|
91
|
+
showResolved?: boolean;
|
|
92
|
+
/** Id of the annotation to render in the "active" style (host-
|
|
93
|
+
* controlled hover / selection affordance). */
|
|
94
|
+
activeAnnotationId?: string;
|
|
95
|
+
/** Optional click callback. Hosts wire it to open their own
|
|
96
|
+
* popover / sidebar showing the annotation body, author, etc. */
|
|
97
|
+
onSelect?: (annotation: AnnotationOverlayAnnotation) => void;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Build a screen-reader-friendly description of an annotation.
|
|
101
|
+
*
|
|
102
|
+
* Pattern: `"<status> <kind> annotation[: <truncated body>]"`. The
|
|
103
|
+
* resolved status comes first so screen readers announce it ahead of
|
|
104
|
+
* the body (matching the visual cue: greyed-out marker = resolved).
|
|
105
|
+
* Body text is truncated to 60 chars so long comments don't blow up
|
|
106
|
+
* the announcement. The internal `id` is intentionally omitted —
|
|
107
|
+
* opaque ids carry no meaning for assistive tech.
|
|
108
|
+
*
|
|
109
|
+
* Pure function; exposed so hosts that render their own annotation
|
|
110
|
+
* affordances (e.g. a sidebar list) can describe rows consistently
|
|
111
|
+
* with the overlay's pin labels.
|
|
112
|
+
*
|
|
113
|
+
* @public
|
|
114
|
+
*/
|
|
115
|
+
export declare function describeAnnotation(annotation: AnnotationOverlayAnnotation): string;
|
|
116
|
+
/**
|
|
117
|
+
* Filter resolved annotations unless `showResolved` is set. Pure
|
|
118
|
+
* function; exposed for hosts that drive their own renderer.
|
|
119
|
+
*
|
|
120
|
+
* @public
|
|
121
|
+
*/
|
|
122
|
+
export declare function visibleAnnotations(annotations: readonly AnnotationOverlayAnnotation[], showResolved: boolean): readonly AnnotationOverlayAnnotation[];
|
|
123
|
+
/**
|
|
124
|
+
* Hit-test an annotation at a point. Point / text annotations use a
|
|
125
|
+
* circular hit zone of {@link HIT_RADIUS_PX} around `(x, y)`; area
|
|
126
|
+
* annotations use the inclusive bounding rectangle. Pure function;
|
|
127
|
+
* exposed so hosts can hit-test against the same algorithm the
|
|
128
|
+
* overlay uses for click handling.
|
|
129
|
+
*
|
|
130
|
+
* @public
|
|
131
|
+
*/
|
|
132
|
+
export declare function isPointInsideAnnotation(annotation: AnnotationOverlayAnnotation, px: number, py: number): boolean;
|
|
133
|
+
/**
|
|
134
|
+
* Renders the annotation layer as an absolutely-positioned SVG over
|
|
135
|
+
* the page surface. Pointer events propagate through transparent
|
|
136
|
+
* areas; hosts can stack the overlay over interactive layers without
|
|
137
|
+
* blocking selection of canvas objects.
|
|
138
|
+
*
|
|
139
|
+
* @public
|
|
140
|
+
*/
|
|
141
|
+
export declare function AnnotationOverlay({ annotations, pageWidthPx, pageHeightPx, showResolved, activeAnnotationId, onSelect, }: AnnotationOverlayProps): ReactElement;
|
|
142
|
+
//# sourceMappingURL=AnnotationOverlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnnotationOverlay.d.ts","sourceRoot":"","sources":["../../src/components/AnnotationOverlay.tsx"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,OAAO,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,GAAG;IACvD,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,GAAG;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,GAAG;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GACnC,oBAAoB,GACpB,mBAAmB,GACnB,mBAAmB,CAAC;AAExB;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,mEAAmE;IACnE,WAAW,EAAE,SAAS,2BAA2B,EAAE,CAAC;IACpD;;2CAEuC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,YAAY,EAAE,MAAM,CAAC;IACrB;;0DAEsD;IACtD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;oDACgD;IAChD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;sEACkE;IAClE,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,2BAA2B,KAAK,IAAI,CAAC;CAC9D,CAAC;AAsBF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,2BAA2B,GAAG,MAAM,CAOlF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,SAAS,2BAA2B,EAAE,EACnD,YAAY,EAAE,OAAO,GACpB,SAAS,2BAA2B,EAAE,CAGxC;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,2BAA2B,EACvC,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT,OAAO,CAYT;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,WAAW,EACX,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,QAAQ,GACT,EAAE,sBAAsB,GAAG,YAAY,CAsCvC"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
"use client";
|
|
3
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
4
|
+
/**
|
|
5
|
+
* Pixel hit radius around a point / text annotation's anchor. Pretty
|
|
6
|
+
* small so adjacent pins don't fight; hosts that want larger targets
|
|
7
|
+
* can scale the overlay with CSS transforms.
|
|
8
|
+
*/
|
|
9
|
+
const HIT_RADIUS_PX = 8;
|
|
10
|
+
/** Visual marker size (radius) for point + text pins. Slightly
|
|
11
|
+
* smaller than the hit radius so the cursor finds the center even
|
|
12
|
+
* on the marker's edge. */
|
|
13
|
+
const MARKER_RADIUS_PX = 6;
|
|
14
|
+
const COLORS = {
|
|
15
|
+
open: "#dc2626",
|
|
16
|
+
resolved: "#9ca3af",
|
|
17
|
+
active: "#2563eb",
|
|
18
|
+
textBackground: "#fde68a",
|
|
19
|
+
textForeground: "#78350f",
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Build a screen-reader-friendly description of an annotation.
|
|
23
|
+
*
|
|
24
|
+
* Pattern: `"<status> <kind> annotation[: <truncated body>]"`. The
|
|
25
|
+
* resolved status comes first so screen readers announce it ahead of
|
|
26
|
+
* the body (matching the visual cue: greyed-out marker = resolved).
|
|
27
|
+
* Body text is truncated to 60 chars so long comments don't blow up
|
|
28
|
+
* the announcement. The internal `id` is intentionally omitted —
|
|
29
|
+
* opaque ids carry no meaning for assistive tech.
|
|
30
|
+
*
|
|
31
|
+
* Pure function; exposed so hosts that render their own annotation
|
|
32
|
+
* affordances (e.g. a sidebar list) can describe rows consistently
|
|
33
|
+
* with the overlay's pin labels.
|
|
34
|
+
*
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
export function describeAnnotation(annotation) {
|
|
38
|
+
const status = annotation.resolved ? "Resolved" : "Open";
|
|
39
|
+
const body = "text" in annotation && annotation.text ? annotation.text : undefined;
|
|
40
|
+
const truncatedBody = body !== undefined && body.length > 60 ? `${body.slice(0, 60)}…` : body;
|
|
41
|
+
return truncatedBody
|
|
42
|
+
? `${status} ${annotation.kind} annotation: ${truncatedBody}`
|
|
43
|
+
: `${status} ${annotation.kind} annotation`;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Filter resolved annotations unless `showResolved` is set. Pure
|
|
47
|
+
* function; exposed for hosts that drive their own renderer.
|
|
48
|
+
*
|
|
49
|
+
* @public
|
|
50
|
+
*/
|
|
51
|
+
export function visibleAnnotations(annotations, showResolved) {
|
|
52
|
+
if (showResolved)
|
|
53
|
+
return annotations;
|
|
54
|
+
return annotations.filter((a) => !a.resolved);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Hit-test an annotation at a point. Point / text annotations use a
|
|
58
|
+
* circular hit zone of {@link HIT_RADIUS_PX} around `(x, y)`; area
|
|
59
|
+
* annotations use the inclusive bounding rectangle. Pure function;
|
|
60
|
+
* exposed so hosts can hit-test against the same algorithm the
|
|
61
|
+
* overlay uses for click handling.
|
|
62
|
+
*
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
65
|
+
export function isPointInsideAnnotation(annotation, px, py) {
|
|
66
|
+
if (annotation.kind === "area") {
|
|
67
|
+
return (px >= annotation.x &&
|
|
68
|
+
px <= annotation.x + annotation.width &&
|
|
69
|
+
py >= annotation.y &&
|
|
70
|
+
py <= annotation.y + annotation.height);
|
|
71
|
+
}
|
|
72
|
+
const dx = px - annotation.x;
|
|
73
|
+
const dy = py - annotation.y;
|
|
74
|
+
return dx * dx + dy * dy <= HIT_RADIUS_PX * HIT_RADIUS_PX;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Renders the annotation layer as an absolutely-positioned SVG over
|
|
78
|
+
* the page surface. Pointer events propagate through transparent
|
|
79
|
+
* areas; hosts can stack the overlay over interactive layers without
|
|
80
|
+
* blocking selection of canvas objects.
|
|
81
|
+
*
|
|
82
|
+
* @public
|
|
83
|
+
*/
|
|
84
|
+
export function AnnotationOverlay({ annotations, pageWidthPx, pageHeightPx, showResolved, activeAnnotationId, onSelect, }) {
|
|
85
|
+
const visible = visibleAnnotations(annotations, showResolved ?? false);
|
|
86
|
+
return (_jsx("svg", { "data-testid": "annotation-overlay", width: pageWidthPx, height: pageHeightPx, viewBox: `0 0 ${pageWidthPx} ${pageHeightPx}`, style: {
|
|
87
|
+
position: "absolute",
|
|
88
|
+
top: 0,
|
|
89
|
+
left: 0,
|
|
90
|
+
pointerEvents: "none",
|
|
91
|
+
},
|
|
92
|
+
// `role="img"` declares "no semantic children" — browsers apply
|
|
93
|
+
// role="presentation" to every descendant, which would hide the
|
|
94
|
+
// interactive markers from screen readers. When `onSelect` is
|
|
95
|
+
// wired we switch to `role="group"` so the buttons stay reachable;
|
|
96
|
+
// the static (read-only) case keeps `role="img"` so a screen
|
|
97
|
+
// reader announces "Page annotations: 5 visible" as a single
|
|
98
|
+
// labelled image, not a navigable list.
|
|
99
|
+
role: onSelect ? "group" : "img", "aria-label": visible.length === 0
|
|
100
|
+
? "Page annotations: none visible"
|
|
101
|
+
: `Page annotations: ${visible.length} visible`, children: visible.map((a) => (_jsx(AnnotationMarker, { annotation: a, isActive: a.id === activeAnnotationId, onSelect: onSelect }, a.id))) }));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Renders a single annotation marker. Switches on `kind`: area
|
|
105
|
+
* draws a translucent rectangle, point + text draw a circular pin.
|
|
106
|
+
* Text annotations get a small badge so the operator can distinguish
|
|
107
|
+
* them from notes-optional points at a glance.
|
|
108
|
+
*
|
|
109
|
+
* Intra-package helper — not exported.
|
|
110
|
+
*/
|
|
111
|
+
function AnnotationMarker({ annotation, isActive, onSelect, }) {
|
|
112
|
+
const color = isActive ? COLORS.active : annotation.resolved ? COLORS.resolved : COLORS.open;
|
|
113
|
+
const cursor = onSelect ? "pointer" : "default";
|
|
114
|
+
const handleClick = onSelect ? () => onSelect(annotation) : undefined;
|
|
115
|
+
// Enter / Space activate the marker via keyboard — keeps the SVG
|
|
116
|
+
// overlay accessible to keyboard-only users while the host still
|
|
117
|
+
// owns higher-level focus management (tab order, roving index).
|
|
118
|
+
const handleKey = onSelect
|
|
119
|
+
? (e) => {
|
|
120
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
onSelect(annotation);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
: undefined;
|
|
126
|
+
const interactiveProps = onSelect
|
|
127
|
+
? {
|
|
128
|
+
role: "button",
|
|
129
|
+
tabIndex: 0,
|
|
130
|
+
"aria-label": describeAnnotation(annotation),
|
|
131
|
+
}
|
|
132
|
+
: {};
|
|
133
|
+
if (annotation.kind === "area") {
|
|
134
|
+
return (_jsx("g", { "data-testid": `annotation-${annotation.id}`, style: { pointerEvents: "auto", cursor }, onClick: handleClick, onKeyDown: handleKey, ...interactiveProps, children: _jsx("rect", { x: annotation.x, y: annotation.y, width: annotation.width, height: annotation.height, fill: color, fillOpacity: 0.1, stroke: color, strokeWidth: isActive ? 2 : 1, strokeDasharray: annotation.resolved ? "4 2" : undefined }) }));
|
|
135
|
+
}
|
|
136
|
+
const isTextKind = annotation.kind === "text";
|
|
137
|
+
return (_jsxs("g", { "data-testid": `annotation-${annotation.id}`, style: { pointerEvents: "auto", cursor }, onClick: handleClick, onKeyDown: handleKey, ...interactiveProps, children: [_jsx("circle", { cx: annotation.x, cy: annotation.y, r: MARKER_RADIUS_PX, fill: isTextKind ? COLORS.textBackground : color, stroke: color, strokeWidth: isActive ? 2 : 1 }), isTextKind && (
|
|
138
|
+
// biome-ignore lint/a11y/noAriaHiddenOnFocusable: nested SVG <text> inside a focusable <g role="button"> is treated as focusable by the rule, but it's a purely decorative badge and the parent owns the a11y contract — without aria-hidden a screen reader would redundantly announce a literal "T".
|
|
139
|
+
_jsx("text", { x: annotation.x, y: annotation.y + 3, textAnchor: "middle", fontSize: "7", fontWeight: "bold", fill: COLORS.textForeground, "aria-hidden": "true", children: "T" }))] }));
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=AnnotationOverlay.js.map
|