@joewinke/jatui 0.36.3 → 0.38.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/dist/artifact-blocks/AttachmentChip.svelte +66 -0
- package/dist/artifact-blocks/AttachmentChip.svelte.d.ts +7 -0
- package/dist/artifact-blocks/BlockComments.svelte +309 -0
- package/dist/artifact-blocks/BlockComments.svelte.d.ts +33 -0
- package/dist/artifact-blocks/BlockRenderer.svelte +674 -0
- package/dist/artifact-blocks/BlockRenderer.svelte.d.ts +55 -0
- package/dist/artifact-blocks/BlockVerdict.svelte +151 -0
- package/dist/artifact-blocks/BlockVerdict.svelte.d.ts +13 -0
- package/dist/artifact-blocks/BlocksArtifactView.svelte +1616 -0
- package/dist/artifact-blocks/BlocksArtifactView.svelte.d.ts +113 -0
- package/dist/artifact-blocks/CONTRACT.md +225 -0
- package/dist/artifact-blocks/CommentPill.svelte +49 -0
- package/dist/artifact-blocks/CommentPill.svelte.d.ts +16 -0
- package/dist/artifact-blocks/FoldedRounds.svelte +95 -0
- package/dist/artifact-blocks/FoldedRounds.svelte.d.ts +9 -0
- package/dist/artifact-blocks/Glyph.svelte +54 -0
- package/dist/artifact-blocks/Glyph.svelte.d.ts +10 -0
- package/dist/artifact-blocks/HistoryScrubber.svelte +500 -0
- package/dist/artifact-blocks/HistoryScrubber.svelte.d.ts +15 -0
- package/dist/artifact-blocks/Markdown.svelte +186 -0
- package/dist/artifact-blocks/Markdown.svelte.d.ts +30 -0
- package/dist/artifact-blocks/NoteComposer.svelte +31 -0
- package/dist/artifact-blocks/NoteComposer.svelte.d.ts +8 -0
- package/dist/artifact-blocks/PlainComposer.svelte +200 -0
- package/dist/artifact-blocks/PlainComposer.svelte.d.ts +15 -0
- package/dist/artifact-blocks/SectionTools.svelte +102 -0
- package/dist/artifact-blocks/SectionTools.svelte.d.ts +19 -0
- package/dist/artifact-blocks/adapter.d.ts +13 -0
- package/dist/artifact-blocks/adapter.js +31 -0
- package/dist/artifact-blocks/autoSend.d.ts +49 -0
- package/dist/artifact-blocks/autoSend.js +103 -0
- package/dist/artifact-blocks/blocks/actions/ActionsBlock.svelte +557 -0
- package/dist/artifact-blocks/blocks/actions/ActionsBlock.svelte.d.ts +48 -0
- package/dist/artifact-blocks/blocks/actions/block.d.ts +8 -0
- package/dist/artifact-blocks/blocks/actions/block.js +74 -0
- package/dist/artifact-blocks/blocks/actions/requests.d.ts +62 -0
- package/dist/artifact-blocks/blocks/actions/requests.js +26 -0
- package/dist/artifact-blocks/blocks/actions/schema.d.ts +168 -0
- package/dist/artifact-blocks/blocks/actions/schema.js +253 -0
- package/dist/artifact-blocks/blocks/callout/CalloutBlock.svelte +779 -0
- package/dist/artifact-blocks/blocks/callout/CalloutBlock.svelte.d.ts +40 -0
- package/dist/artifact-blocks/blocks/callout/block.d.ts +7 -0
- package/dist/artifact-blocks/blocks/callout/block.js +39 -0
- package/dist/artifact-blocks/blocks/callout/schema.d.ts +72 -0
- package/dist/artifact-blocks/blocks/callout/schema.js +115 -0
- package/dist/artifact-blocks/blocks/chart/ChartBlock.svelte +875 -0
- package/dist/artifact-blocks/blocks/chart/ChartBlock.svelte.d.ts +59 -0
- package/dist/artifact-blocks/blocks/chart/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/chart/block.js +70 -0
- package/dist/artifact-blocks/blocks/chart/schema.d.ts +171 -0
- package/dist/artifact-blocks/blocks/chart/schema.js +498 -0
- package/dist/artifact-blocks/blocks/chart/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/chart/thumb.js +201 -0
- package/dist/artifact-blocks/blocks/checklist/ChecklistBlock.svelte +1048 -0
- package/dist/artifact-blocks/blocks/checklist/ChecklistBlock.svelte.d.ts +16 -0
- package/dist/artifact-blocks/blocks/checklist/Icon.svelte +105 -0
- package/dist/artifact-blocks/blocks/checklist/Icon.svelte.d.ts +17 -0
- package/dist/artifact-blocks/blocks/checklist/block.d.ts +10 -0
- package/dist/artifact-blocks/blocks/checklist/block.js +63 -0
- package/dist/artifact-blocks/blocks/checklist/itemReview.d.ts +16 -0
- package/dist/artifact-blocks/blocks/checklist/itemReview.js +63 -0
- package/dist/artifact-blocks/blocks/checklist/labels.d.ts +62 -0
- package/dist/artifact-blocks/blocks/checklist/labels.js +96 -0
- package/dist/artifact-blocks/blocks/checklist/retest.d.ts +43 -0
- package/dist/artifact-blocks/blocks/checklist/retest.js +68 -0
- package/dist/artifact-blocks/blocks/checklist/schema.d.ts +84 -0
- package/dist/artifact-blocks/blocks/checklist/schema.js +91 -0
- package/dist/artifact-blocks/blocks/code/CodeBlock.svelte +128 -0
- package/dist/artifact-blocks/blocks/code/CodeBlock.svelte.d.ts +12 -0
- package/dist/artifact-blocks/blocks/code/block.d.ts +8 -0
- package/dist/artifact-blocks/blocks/code/block.js +31 -0
- package/dist/artifact-blocks/blocks/code/highlight.d.ts +34 -0
- package/dist/artifact-blocks/blocks/code/highlight.js +280 -0
- package/dist/artifact-blocks/blocks/code/schema.d.ts +32 -0
- package/dist/artifact-blocks/blocks/code/schema.js +39 -0
- package/dist/artifact-blocks/blocks/collage/CollageBlock.svelte +290 -0
- package/dist/artifact-blocks/blocks/collage/CollageBlock.svelte.d.ts +29 -0
- package/dist/artifact-blocks/blocks/collage/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/collage/block.js +29 -0
- package/dist/artifact-blocks/blocks/collage/schema.d.ts +52 -0
- package/dist/artifact-blocks/blocks/collage/schema.js +86 -0
- package/dist/artifact-blocks/blocks/compare/CompareBlock.svelte +484 -0
- package/dist/artifact-blocks/blocks/compare/CompareBlock.svelte.d.ts +25 -0
- package/dist/artifact-blocks/blocks/compare/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/compare/block.js +32 -0
- package/dist/artifact-blocks/blocks/compare/schema.d.ts +74 -0
- package/dist/artifact-blocks/blocks/compare/schema.js +148 -0
- package/dist/artifact-blocks/blocks/diagram/DiagramBlock.svelte +392 -0
- package/dist/artifact-blocks/blocks/diagram/DiagramBlock.svelte.d.ts +30 -0
- package/dist/artifact-blocks/blocks/diagram/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/diagram/block.js +50 -0
- package/dist/artifact-blocks/blocks/diagram/constants.d.ts +48 -0
- package/dist/artifact-blocks/blocks/diagram/constants.js +53 -0
- package/dist/artifact-blocks/blocks/diagram/schema.d.ts +161 -0
- package/dist/artifact-blocks/blocks/diagram/schema.js +621 -0
- package/dist/artifact-blocks/blocks/diagram/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/diagram/thumb.js +157 -0
- package/dist/artifact-blocks/blocks/diagram/types.d.ts +74 -0
- package/dist/artifact-blocks/blocks/diagram/types.js +1 -0
- package/dist/artifact-blocks/blocks/diff/DiffBlock.svelte +81 -0
- package/dist/artifact-blocks/blocks/diff/DiffBlock.svelte.d.ts +10 -0
- package/dist/artifact-blocks/blocks/diff/block.d.ts +7 -0
- package/dist/artifact-blocks/blocks/diff/block.js +36 -0
- package/dist/artifact-blocks/blocks/diff/schema.d.ts +18 -0
- package/dist/artifact-blocks/blocks/diff/schema.js +35 -0
- package/dist/artifact-blocks/blocks/diff-review/DiffReviewBlock.svelte +1055 -0
- package/dist/artifact-blocks/blocks/diff-review/DiffReviewBlock.svelte.d.ts +5 -0
- package/dist/artifact-blocks/blocks/diff-review/block.d.ts +15 -0
- package/dist/artifact-blocks/blocks/diff-review/block.js +64 -0
- package/dist/artifact-blocks/blocks/diff-review/decisions.d.ts +57 -0
- package/dist/artifact-blocks/blocks/diff-review/decisions.js +80 -0
- package/dist/artifact-blocks/blocks/diff-review/schema.d.ts +13 -0
- package/dist/artifact-blocks/blocks/diff-review/schema.js +84 -0
- package/dist/artifact-blocks/blocks/diff-review/types.d.ts +35 -0
- package/dist/artifact-blocks/blocks/diff-review/types.js +1 -0
- package/dist/artifact-blocks/blocks/explainer/ExplainerBlock.svelte +145 -0
- package/dist/artifact-blocks/blocks/explainer/ExplainerBlock.svelte.d.ts +5 -0
- package/dist/artifact-blocks/blocks/explainer/block.d.ts +8 -0
- package/dist/artifact-blocks/blocks/explainer/block.js +21 -0
- package/dist/artifact-blocks/blocks/explainer/schema.d.ts +26 -0
- package/dist/artifact-blocks/blocks/explainer/schema.js +69 -0
- package/dist/artifact-blocks/blocks/group-content.d.ts +10 -0
- package/dist/artifact-blocks/blocks/group-content.js +16 -0
- package/dist/artifact-blocks/blocks/group-data.d.ts +10 -0
- package/dist/artifact-blocks/blocks/group-data.js +7 -0
- package/dist/artifact-blocks/blocks/group-review.d.ts +10 -0
- package/dist/artifact-blocks/blocks/group-review.js +12 -0
- package/dist/artifact-blocks/blocks/group-visual.d.ts +17 -0
- package/dist/artifact-blocks/blocks/group-visual.js +14 -0
- package/dist/artifact-blocks/blocks/map/MapBlock.svelte +413 -0
- package/dist/artifact-blocks/blocks/map/MapBlock.svelte.d.ts +53 -0
- package/dist/artifact-blocks/blocks/map/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/map/block.js +62 -0
- package/dist/artifact-blocks/blocks/map/schema.d.ts +123 -0
- package/dist/artifact-blocks/blocks/map/schema.js +113 -0
- package/dist/artifact-blocks/blocks/map/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/map/thumb.js +69 -0
- package/dist/artifact-blocks/blocks/markdown/MarkdownBlock.svelte +33 -0
- package/dist/artifact-blocks/blocks/markdown/MarkdownBlock.svelte.d.ts +15 -0
- package/dist/artifact-blocks/blocks/markdown/block.d.ts +7 -0
- package/dist/artifact-blocks/blocks/markdown/block.js +28 -0
- package/dist/artifact-blocks/blocks/markdown/render.d.ts +17 -0
- package/dist/artifact-blocks/blocks/markdown/render.js +115 -0
- package/dist/artifact-blocks/blocks/markdown/schema.d.ts +10 -0
- package/dist/artifact-blocks/blocks/markdown/schema.js +23 -0
- package/dist/artifact-blocks/blocks/media-picker/MediaPickerBlock.svelte +2112 -0
- package/dist/artifact-blocks/blocks/media-picker/MediaPickerBlock.svelte.d.ts +5 -0
- package/dist/artifact-blocks/blocks/media-picker/block.d.ts +24 -0
- package/dist/artifact-blocks/blocks/media-picker/block.js +137 -0
- package/dist/artifact-blocks/blocks/media-picker/choices.d.ts +28 -0
- package/dist/artifact-blocks/blocks/media-picker/choices.js +35 -0
- package/dist/artifact-blocks/blocks/media-picker/decisions.d.ts +51 -0
- package/dist/artifact-blocks/blocks/media-picker/decisions.js +75 -0
- package/dist/artifact-blocks/blocks/media-picker/schema.d.ts +7 -0
- package/dist/artifact-blocks/blocks/media-picker/schema.js +199 -0
- package/dist/artifact-blocks/blocks/media-picker/types.d.ts +92 -0
- package/dist/artifact-blocks/blocks/media-picker/types.js +1 -0
- package/dist/artifact-blocks/blocks/media-picker/versions.d.ts +51 -0
- package/dist/artifact-blocks/blocks/media-picker/versions.js +118 -0
- package/dist/artifact-blocks/blocks/mockup/MockupBlock.svelte +213 -0
- package/dist/artifact-blocks/blocks/mockup/MockupBlock.svelte.d.ts +5 -0
- package/dist/artifact-blocks/blocks/mockup/block.d.ts +26 -0
- package/dist/artifact-blocks/blocks/mockup/block.js +52 -0
- package/dist/artifact-blocks/blocks/mockup/doc.d.ts +24 -0
- package/dist/artifact-blocks/blocks/mockup/doc.js +44 -0
- package/dist/artifact-blocks/blocks/mockup/sandbox.d.ts +21 -0
- package/dist/artifact-blocks/blocks/mockup/sandbox.js +27 -0
- package/dist/artifact-blocks/blocks/mockup/schema.d.ts +7 -0
- package/dist/artifact-blocks/blocks/mockup/schema.js +90 -0
- package/dist/artifact-blocks/blocks/mockup/types.d.ts +36 -0
- package/dist/artifact-blocks/blocks/mockup/types.js +17 -0
- package/dist/artifact-blocks/blocks/program/ProgramBlock.svelte +359 -0
- package/dist/artifact-blocks/blocks/program/ProgramBlock.svelte.d.ts +32 -0
- package/dist/artifact-blocks/blocks/program/block.d.ts +35 -0
- package/dist/artifact-blocks/blocks/program/block.js +68 -0
- package/dist/artifact-blocks/blocks/program/schema.d.ts +132 -0
- package/dist/artifact-blocks/blocks/program/schema.js +268 -0
- package/dist/artifact-blocks/blocks/stats/StatsBlock.svelte +315 -0
- package/dist/artifact-blocks/blocks/stats/StatsBlock.svelte.d.ts +24 -0
- package/dist/artifact-blocks/blocks/stats/block.d.ts +7 -0
- package/dist/artifact-blocks/blocks/stats/block.js +49 -0
- package/dist/artifact-blocks/blocks/stats/schema.d.ts +82 -0
- package/dist/artifact-blocks/blocks/stats/schema.js +43 -0
- package/dist/artifact-blocks/blocks/stats/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/stats/thumb.js +102 -0
- package/dist/artifact-blocks/blocks/table/TableBlock.svelte +490 -0
- package/dist/artifact-blocks/blocks/table/TableBlock.svelte.d.ts +28 -0
- package/dist/artifact-blocks/blocks/table/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/table/block.js +100 -0
- package/dist/artifact-blocks/blocks/table/schema.d.ts +143 -0
- package/dist/artifact-blocks/blocks/table/schema.js +278 -0
- package/dist/artifact-blocks/blocks/timeline/TimelineBlock.svelte +347 -0
- package/dist/artifact-blocks/blocks/timeline/TimelineBlock.svelte.d.ts +42 -0
- package/dist/artifact-blocks/blocks/timeline/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/timeline/block.js +96 -0
- package/dist/artifact-blocks/blocks/timeline/constants.d.ts +26 -0
- package/dist/artifact-blocks/blocks/timeline/constants.js +27 -0
- package/dist/artifact-blocks/blocks/timeline/schema.d.ts +102 -0
- package/dist/artifact-blocks/blocks/timeline/schema.js +405 -0
- package/dist/artifact-blocks/blocks/timeline/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/timeline/thumb.js +117 -0
- package/dist/artifact-blocks/blocks/timeline/types.d.ts +66 -0
- package/dist/artifact-blocks/blocks/timeline/types.js +1 -0
- package/dist/artifact-blocks/blocks/transcript/TranscriptBlock.svelte +169 -0
- package/dist/artifact-blocks/blocks/transcript/TranscriptBlock.svelte.d.ts +26 -0
- package/dist/artifact-blocks/blocks/transcript/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/transcript/block.js +86 -0
- package/dist/artifact-blocks/blocks/transcript/schema.d.ts +114 -0
- package/dist/artifact-blocks/blocks/transcript/schema.js +126 -0
- package/dist/artifact-blocks/builtins.d.ts +40 -0
- package/dist/artifact-blocks/builtins.js +22 -0
- package/dist/artifact-blocks/contractBlocks.d.ts +92 -0
- package/dist/artifact-blocks/contractBlocks.js +279 -0
- package/dist/artifact-blocks/history.d.ts +140 -0
- package/dist/artifact-blocks/history.js +363 -0
- package/dist/artifact-blocks/index.d.ts +40 -0
- package/dist/artifact-blocks/index.js +46 -0
- package/dist/artifact-blocks/listen/context.d.ts +30 -0
- package/dist/artifact-blocks/listen/context.js +30 -0
- package/dist/artifact-blocks/liveData.d.ts +78 -0
- package/dist/artifact-blocks/liveData.js +137 -0
- package/dist/artifact-blocks/liveDataPoller.d.ts +41 -0
- package/dist/artifact-blocks/liveDataPoller.js +140 -0
- package/dist/artifact-blocks/openNav.d.ts +46 -0
- package/dist/artifact-blocks/openNav.js +106 -0
- package/dist/artifact-blocks/pageReviewKeys.d.ts +17 -0
- package/dist/artifact-blocks/pageReviewKeys.js +115 -0
- package/dist/artifact-blocks/params.d.ts +98 -0
- package/dist/artifact-blocks/params.js +159 -0
- package/dist/artifact-blocks/registry.d.ts +27 -0
- package/dist/artifact-blocks/registry.js +83 -0
- package/dist/artifact-blocks/review/BlockIcon.svelte +99 -0
- package/dist/artifact-blocks/review/BlockIcon.svelte.d.ts +17 -0
- package/dist/artifact-blocks/review/BulkRejectForm.svelte +94 -0
- package/dist/artifact-blocks/review/BulkRejectForm.svelte.d.ts +11 -0
- package/dist/artifact-blocks/review/BulkUndoBar.svelte +40 -0
- package/dist/artifact-blocks/review/BulkUndoBar.svelte.d.ts +9 -0
- package/dist/artifact-blocks/review/ItemNote.svelte +193 -0
- package/dist/artifact-blocks/review/ItemNote.svelte.d.ts +33 -0
- package/dist/artifact-blocks/review/NoteToggle.svelte +52 -0
- package/dist/artifact-blocks/review/NoteToggle.svelte.d.ts +12 -0
- package/dist/artifact-blocks/review/TeamChip.svelte +71 -0
- package/dist/artifact-blocks/review/TeamChip.svelte.d.ts +8 -0
- package/dist/artifact-blocks/review/attachments.d.ts +10 -0
- package/dist/artifact-blocks/review/attachments.js +37 -0
- package/dist/artifact-blocks/review/bulk.d.ts +52 -0
- package/dist/artifact-blocks/review/bulk.js +61 -0
- package/dist/artifact-blocks/review/itemReview.d.ts +33 -0
- package/dist/artifact-blocks/review/itemReview.js +112 -0
- package/dist/artifact-blocks/review/roundLines.d.ts +14 -0
- package/dist/artifact-blocks/review/roundLines.js +20 -0
- package/dist/artifact-blocks/review/rounds.d.ts +86 -0
- package/dist/artifact-blocks/review/rounds.js +302 -0
- package/dist/artifact-blocks/review/saver.d.ts +23 -0
- package/dist/artifact-blocks/review/saver.js +48 -0
- package/dist/artifact-blocks/review/team.d.ts +31 -0
- package/dist/artifact-blocks/review/team.js +42 -0
- package/dist/artifact-blocks/review/visualOrder.d.ts +29 -0
- package/dist/artifact-blocks/review/visualOrder.js +66 -0
- package/dist/artifact-blocks/review.d.ts +180 -0
- package/dist/artifact-blocks/review.js +716 -0
- package/dist/artifact-blocks/thumb-kit.d.ts +80 -0
- package/dist/artifact-blocks/thumb-kit.js +149 -0
- package/dist/artifact-blocks/types.d.ts +513 -0
- package/dist/artifact-blocks/types.js +1 -0
- package/dist/components/Button.svelte +8 -0
- package/dist/components/TextInput.svelte +39 -6
- package/dist/components/TextInput.svelte.d.ts +15 -7
- package/dist/components/basic/Checkbox.svelte +52 -14
- package/dist/components/basic/Checkbox.svelte.d.ts +11 -1
- package/package.json +11 -2
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Params schema for the map block (Google map of markers + optional routes).
|
|
3
|
+
* Imported by block.ts (def), MapBlock.svelte (props typing) and the tests —
|
|
4
|
+
* keeps them cycle-free.
|
|
5
|
+
*
|
|
6
|
+
* Tone is a THEME token (never a color literal): the component resolves it
|
|
7
|
+
* against the live theme's CSS vars at render, because Google's canvas sits
|
|
8
|
+
* outside the DOM cascade and cannot read `var(--color-*)` directly.
|
|
9
|
+
*/
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
declare const mapMarkerSchema: z.ZodObject<{
|
|
12
|
+
label: z.ZodString;
|
|
13
|
+
lat: z.ZodNumber;
|
|
14
|
+
lng: z.ZodNumber;
|
|
15
|
+
tone: z.ZodOptional<z.ZodEnum<{
|
|
16
|
+
error: "error";
|
|
17
|
+
warning: "warning";
|
|
18
|
+
info: "info";
|
|
19
|
+
success: "success";
|
|
20
|
+
primary: "primary";
|
|
21
|
+
secondary: "secondary";
|
|
22
|
+
neutral: "neutral";
|
|
23
|
+
accent: "accent";
|
|
24
|
+
}>>;
|
|
25
|
+
popup: z.ZodOptional<z.ZodString>;
|
|
26
|
+
address: z.ZodOptional<z.ZodString>;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
declare const mapRouteSchema: z.ZodObject<{
|
|
29
|
+
label: z.ZodOptional<z.ZodString>;
|
|
30
|
+
tone: z.ZodOptional<z.ZodEnum<{
|
|
31
|
+
error: "error";
|
|
32
|
+
warning: "warning";
|
|
33
|
+
info: "info";
|
|
34
|
+
success: "success";
|
|
35
|
+
primary: "primary";
|
|
36
|
+
secondary: "secondary";
|
|
37
|
+
neutral: "neutral";
|
|
38
|
+
accent: "accent";
|
|
39
|
+
}>>;
|
|
40
|
+
points: z.ZodArray<z.ZodObject<{
|
|
41
|
+
lat: z.ZodNumber;
|
|
42
|
+
lng: z.ZodNumber;
|
|
43
|
+
}, z.core.$strip>>;
|
|
44
|
+
}, z.core.$strip>;
|
|
45
|
+
export declare const mapSchema: z.ZodObject<{
|
|
46
|
+
title: z.ZodOptional<z.ZodString>;
|
|
47
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
48
|
+
center: z.ZodOptional<z.ZodObject<{
|
|
49
|
+
lat: z.ZodNumber;
|
|
50
|
+
lng: z.ZodNumber;
|
|
51
|
+
}, z.core.$strip>>;
|
|
52
|
+
zoom: z.ZodOptional<z.ZodNumber>;
|
|
53
|
+
fit: z.ZodOptional<z.ZodBoolean>;
|
|
54
|
+
markers: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
55
|
+
label: z.ZodString;
|
|
56
|
+
lat: z.ZodNumber;
|
|
57
|
+
lng: z.ZodNumber;
|
|
58
|
+
tone: z.ZodOptional<z.ZodEnum<{
|
|
59
|
+
error: "error";
|
|
60
|
+
warning: "warning";
|
|
61
|
+
info: "info";
|
|
62
|
+
success: "success";
|
|
63
|
+
primary: "primary";
|
|
64
|
+
secondary: "secondary";
|
|
65
|
+
neutral: "neutral";
|
|
66
|
+
accent: "accent";
|
|
67
|
+
}>>;
|
|
68
|
+
popup: z.ZodOptional<z.ZodString>;
|
|
69
|
+
address: z.ZodOptional<z.ZodString>;
|
|
70
|
+
}, z.core.$strip>>>;
|
|
71
|
+
routes: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
72
|
+
label: z.ZodOptional<z.ZodString>;
|
|
73
|
+
tone: z.ZodOptional<z.ZodEnum<{
|
|
74
|
+
error: "error";
|
|
75
|
+
warning: "warning";
|
|
76
|
+
info: "info";
|
|
77
|
+
success: "success";
|
|
78
|
+
primary: "primary";
|
|
79
|
+
secondary: "secondary";
|
|
80
|
+
neutral: "neutral";
|
|
81
|
+
accent: "accent";
|
|
82
|
+
}>>;
|
|
83
|
+
points: z.ZodArray<z.ZodObject<{
|
|
84
|
+
lat: z.ZodNumber;
|
|
85
|
+
lng: z.ZodNumber;
|
|
86
|
+
}, z.core.$strip>>;
|
|
87
|
+
}, z.core.$strip>>>;
|
|
88
|
+
}, z.core.$strip>;
|
|
89
|
+
export type MapMarker = z.infer<typeof mapMarkerSchema>;
|
|
90
|
+
export type MapRoute = z.infer<typeof mapRouteSchema>;
|
|
91
|
+
export type MapParams = z.infer<typeof mapSchema>;
|
|
92
|
+
/** Defaults applied at render (the host stores what was published verbatim). */
|
|
93
|
+
export declare const MAP_DEFAULTS: {
|
|
94
|
+
readonly height: 420;
|
|
95
|
+
readonly zoom: 12;
|
|
96
|
+
readonly fit: true;
|
|
97
|
+
readonly markerTone: "primary";
|
|
98
|
+
readonly routeTone: "info";
|
|
99
|
+
};
|
|
100
|
+
/** Normalized params with every default applied. */
|
|
101
|
+
export declare function normalizeMap(params: MapParams): Required<Pick<MapParams, "height" | "zoom" | "fit">> & {
|
|
102
|
+
markers: MapMarker[];
|
|
103
|
+
routes: MapRoute[];
|
|
104
|
+
center: MapParams["center"];
|
|
105
|
+
title: MapParams["title"];
|
|
106
|
+
};
|
|
107
|
+
/** Marker tone with default filled. */
|
|
108
|
+
export declare function toneOfMarker(m: MapMarker): string;
|
|
109
|
+
/** Route tone with default filled. */
|
|
110
|
+
export declare function toneOfRoute(r: MapRoute): string;
|
|
111
|
+
/** Coordinate caption for the list fallback: "30.2672, -97.7431". */
|
|
112
|
+
export declare function coordCaption(lat: number, lng: number): string;
|
|
113
|
+
/**
|
|
114
|
+
* Bounds of every marker + route point — the fallback "area" caption and the
|
|
115
|
+
* test hook for fit behaviour. Null when there is nothing to fit.
|
|
116
|
+
*/
|
|
117
|
+
export declare function boundsOf(markers: MapMarker[], routes: MapRoute[]): {
|
|
118
|
+
minLat: number;
|
|
119
|
+
maxLat: number;
|
|
120
|
+
minLng: number;
|
|
121
|
+
maxLng: number;
|
|
122
|
+
} | null;
|
|
123
|
+
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Params schema for the map block (Google map of markers + optional routes).
|
|
3
|
+
* Imported by block.ts (def), MapBlock.svelte (props typing) and the tests —
|
|
4
|
+
* keeps them cycle-free.
|
|
5
|
+
*
|
|
6
|
+
* Tone is a THEME token (never a color literal): the component resolves it
|
|
7
|
+
* against the live theme's CSS vars at render, because Google's canvas sits
|
|
8
|
+
* outside the DOM cascade and cannot read `var(--color-*)` directly.
|
|
9
|
+
*/
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { shortTextSchema, toneSchema } from "../../params";
|
|
12
|
+
/** WGS-84 latitude. */
|
|
13
|
+
const latSchema = z.number().min(-90).max(90);
|
|
14
|
+
/** WGS-84 longitude. */
|
|
15
|
+
const lngSchema = z.number().min(-180).max(180);
|
|
16
|
+
const MARKERS_MAX = 50;
|
|
17
|
+
const ROUTES_MAX = 10;
|
|
18
|
+
const ROUTE_POINTS_MAX = 500;
|
|
19
|
+
const mapMarkerSchema = z.object({
|
|
20
|
+
/** Pin label — the click-popup title and the list fallback row. */
|
|
21
|
+
label: z.string().min(1, "marker label is required").max(120),
|
|
22
|
+
lat: latSchema,
|
|
23
|
+
lng: lngSchema,
|
|
24
|
+
/** Theme tone for the pin. Default "primary". */
|
|
25
|
+
tone: toneSchema.optional(),
|
|
26
|
+
/** Popup body text (rendered as text — never HTML). */
|
|
27
|
+
popup: shortTextSchema.optional(),
|
|
28
|
+
/** Second popup line, e.g. a street address. */
|
|
29
|
+
address: shortTextSchema.optional(),
|
|
30
|
+
});
|
|
31
|
+
const mapRouteSchema = z.object({
|
|
32
|
+
/** Route name for the legend / list fallback. */
|
|
33
|
+
label: shortTextSchema.optional(),
|
|
34
|
+
/** Theme tone for the polyline. Default "info". */
|
|
35
|
+
tone: toneSchema.optional(),
|
|
36
|
+
/** Polyline vertices, in draw order. */
|
|
37
|
+
points: z
|
|
38
|
+
.array(z.object({ lat: latSchema, lng: lngSchema }))
|
|
39
|
+
.min(2, "a route needs at least 2 points")
|
|
40
|
+
.max(ROUTE_POINTS_MAX),
|
|
41
|
+
});
|
|
42
|
+
export const mapSchema = z.object({
|
|
43
|
+
/** Optional heading above the map / list. */
|
|
44
|
+
title: z.string().max(120).optional(),
|
|
45
|
+
/** Map height in px (220–800). Default 420. */
|
|
46
|
+
height: z.number().int().min(220).max(800).optional(),
|
|
47
|
+
/** Manual center, used when fit is false (or there is nothing to fit). */
|
|
48
|
+
center: z.object({ lat: latSchema, lng: lngSchema }).optional(),
|
|
49
|
+
/** Manual zoom 1–20, used when fit is false (or nothing to fit). Default 12. */
|
|
50
|
+
zoom: z.number().int().min(1).max(20).optional(),
|
|
51
|
+
/** Fit the view to every marker + route point. Default true. */
|
|
52
|
+
fit: z.boolean().optional(),
|
|
53
|
+
markers: z.array(mapMarkerSchema).max(MARKERS_MAX).optional(),
|
|
54
|
+
routes: z.array(mapRouteSchema).max(ROUTES_MAX).optional(),
|
|
55
|
+
});
|
|
56
|
+
/** Defaults applied at render (the host stores what was published verbatim). */
|
|
57
|
+
export const MAP_DEFAULTS = {
|
|
58
|
+
height: 420,
|
|
59
|
+
zoom: 12,
|
|
60
|
+
fit: true,
|
|
61
|
+
markerTone: "primary",
|
|
62
|
+
routeTone: "info",
|
|
63
|
+
};
|
|
64
|
+
/** Normalized params with every default applied. */
|
|
65
|
+
export function normalizeMap(params) {
|
|
66
|
+
return {
|
|
67
|
+
title: params.title,
|
|
68
|
+
height: params.height ?? MAP_DEFAULTS.height,
|
|
69
|
+
zoom: params.zoom ?? MAP_DEFAULTS.zoom,
|
|
70
|
+
fit: params.fit ?? MAP_DEFAULTS.fit,
|
|
71
|
+
markers: params.markers ?? [],
|
|
72
|
+
routes: params.routes ?? [],
|
|
73
|
+
center: params.center,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** Marker tone with default filled. */
|
|
77
|
+
export function toneOfMarker(m) {
|
|
78
|
+
return m.tone ?? MAP_DEFAULTS.markerTone;
|
|
79
|
+
}
|
|
80
|
+
/** Route tone with default filled. */
|
|
81
|
+
export function toneOfRoute(r) {
|
|
82
|
+
return r.tone ?? MAP_DEFAULTS.routeTone;
|
|
83
|
+
}
|
|
84
|
+
/** Coordinate caption for the list fallback: "30.2672, -97.7431". */
|
|
85
|
+
export function coordCaption(lat, lng) {
|
|
86
|
+
return `${lat.toFixed(4)}, ${lng.toFixed(4)}`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Bounds of every marker + route point — the fallback "area" caption and the
|
|
90
|
+
* test hook for fit behaviour. Null when there is nothing to fit.
|
|
91
|
+
*/
|
|
92
|
+
export function boundsOf(markers, routes) {
|
|
93
|
+
let minLat = Infinity;
|
|
94
|
+
let maxLat = -Infinity;
|
|
95
|
+
let minLng = Infinity;
|
|
96
|
+
let maxLng = -Infinity;
|
|
97
|
+
for (const m of markers) {
|
|
98
|
+
minLat = Math.min(minLat, m.lat);
|
|
99
|
+
maxLat = Math.max(maxLat, m.lat);
|
|
100
|
+
minLng = Math.min(minLng, m.lng);
|
|
101
|
+
maxLng = Math.max(maxLng, m.lng);
|
|
102
|
+
}
|
|
103
|
+
for (const r of routes)
|
|
104
|
+
for (const p of r.points) {
|
|
105
|
+
minLat = Math.min(minLat, p.lat);
|
|
106
|
+
maxLat = Math.max(maxLat, p.lat);
|
|
107
|
+
minLng = Math.min(minLng, p.lng);
|
|
108
|
+
maxLng = Math.max(maxLng, p.lng);
|
|
109
|
+
}
|
|
110
|
+
if (!Number.isFinite(minLat))
|
|
111
|
+
return null;
|
|
112
|
+
return { minLat, maxLat, minLng, maxLng };
|
|
113
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map block thumbnail composer (mos-qkpva).
|
|
3
|
+
*
|
|
4
|
+
* A map cannot render server-side (the real block draws Google's canvas), so
|
|
5
|
+
* the thumbnail is the block's own data as a simplified pin map: markers
|
|
6
|
+
* plotted with an equirectangular fit over the plotted set, routes as tone
|
|
7
|
+
* polylines, no labels (they are dust at cell size) and never a tile fetch —
|
|
8
|
+
* the same "never a fake screenshot" rule the component's fallback list
|
|
9
|
+
* follows, drawn as a map because that is the SHAPE the thumbnail exists to
|
|
10
|
+
* communicate.
|
|
11
|
+
*/
|
|
12
|
+
import { mapSchema, normalizeMap } from "./schema.js";
|
|
13
|
+
import { r1, thumbText, thumbTruncate, toneColor, } from "../../thumb-kit.js";
|
|
14
|
+
const W = 480;
|
|
15
|
+
const H = 300;
|
|
16
|
+
const PAD = 18;
|
|
17
|
+
export function drawMapThumb(raw, palette) {
|
|
18
|
+
const parsed = mapSchema.safeParse(raw);
|
|
19
|
+
if (!parsed.success)
|
|
20
|
+
return null;
|
|
21
|
+
const norm = normalizeMap(parsed.data);
|
|
22
|
+
const points = [
|
|
23
|
+
...norm.markers.map((m) => ({ lat: m.lat, lng: m.lng })),
|
|
24
|
+
...norm.routes.flatMap((r) => r.points),
|
|
25
|
+
];
|
|
26
|
+
if (points.length === 0)
|
|
27
|
+
return null;
|
|
28
|
+
const lats = points.map((p) => p.lat);
|
|
29
|
+
const lngs = points.map((p) => p.lng);
|
|
30
|
+
const latMin = Math.min(...lats);
|
|
31
|
+
const latMax = Math.max(...lats);
|
|
32
|
+
const lngMin = Math.min(...lngs);
|
|
33
|
+
const lngMax = Math.max(...lngs);
|
|
34
|
+
// Pad the fit slightly so edge pins aren't clipped by their own radius.
|
|
35
|
+
const latPad = Math.max((latMax - latMin) * 0.08, 0.004);
|
|
36
|
+
const lngPad = Math.max((lngMax - lngMin) * 0.08, 0.004);
|
|
37
|
+
const spanLat = Math.max(latMax - latMin + latPad * 2, 1e-9);
|
|
38
|
+
const spanLng = Math.max(lngMax - lngMin + lngPad * 2, 1e-9);
|
|
39
|
+
const px = (lng) => PAD + ((lng - lngMin + lngPad) / spanLng) * (W - PAD * 2);
|
|
40
|
+
const py = (lat) => H - PAD - ((lat - latMin + latPad) / spanLat) * (H - PAD * 2);
|
|
41
|
+
const out = [];
|
|
42
|
+
// routes under pins
|
|
43
|
+
for (const route of norm.routes) {
|
|
44
|
+
const color = toneColor(palette, route.tone ?? "info");
|
|
45
|
+
const d = route.points
|
|
46
|
+
.map((p, i) => `${i ? "L" : "M"} ${r1(px(p.lng))} ${r1(py(p.lat))}`)
|
|
47
|
+
.join(" ");
|
|
48
|
+
out.push(`<path d="${d}" fill="none" stroke="${color}" stroke-opacity="0.7" stroke-width="2"/>`);
|
|
49
|
+
}
|
|
50
|
+
// a soft ground so the pins read as "on a map"
|
|
51
|
+
out.unshift(`<rect x="${r1(PAD / 2)}" y="${r1(PAD / 2)}" width="${r1(W - PAD)}" height="${r1(H - PAD)}" rx="10" fill="${palette.ink}" fill-opacity="0.05"/>`);
|
|
52
|
+
for (const m of norm.markers) {
|
|
53
|
+
const color = toneColor(palette, m.tone ?? "primary");
|
|
54
|
+
const cx = r1(px(m.lng));
|
|
55
|
+
const cy = r1(py(m.lat));
|
|
56
|
+
// pin: a circle with a bg-colored core — a map pin silhouette
|
|
57
|
+
out.push(`<circle cx="${cx}" cy="${cy}" r="7" fill="${color}"/>`);
|
|
58
|
+
out.push(`<circle cx="${cx}" cy="${cy}" r="2.5" fill="${palette.bg}"/>`);
|
|
59
|
+
}
|
|
60
|
+
// One title line when present (the fallback list leads with it too).
|
|
61
|
+
if (norm.title)
|
|
62
|
+
out.push(thumbText(PAD, PAD + 8, thumbTruncate(norm.title, 40), {
|
|
63
|
+
size: 12,
|
|
64
|
+
fill: palette.ink,
|
|
65
|
+
opacity: 0.75,
|
|
66
|
+
weight: 600,
|
|
67
|
+
}));
|
|
68
|
+
return `<svg viewBox="0 0 ${W} ${H}">${out.join("")}</svg>`;
|
|
69
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* markdown block — prose (headings, lists, tables, code, links) through the
|
|
4
|
+
* shared sanitized renderer, drawn by the shared <Markdown> prose surface.
|
|
5
|
+
*
|
|
6
|
+
* The block's own Listen control floats at its top-right (SectionTools)
|
|
7
|
+
* and reads THIS block's paragraphs only — named for the block's first
|
|
8
|
+
* heading, so the button says what it will read.
|
|
9
|
+
*/
|
|
10
|
+
import type { BlockProps } from "../../types.js"
|
|
11
|
+
import type { MarkdownParams } from "./schema.js"
|
|
12
|
+
import Markdown from "../../Markdown.svelte"
|
|
13
|
+
import SectionTools from "../../SectionTools.svelte"
|
|
14
|
+
|
|
15
|
+
let { params, ctx }: BlockProps<MarkdownParams> = $props()
|
|
16
|
+
|
|
17
|
+
/** The container's name for its Listen control: the first markdown
|
|
18
|
+
* heading ("Listen to: What this page is"), else a quiet generic. */
|
|
19
|
+
const listenLabel = $derived.by(() => {
|
|
20
|
+
const md = params?.markdown ?? ""
|
|
21
|
+
const heading = md.match(/^#{1,6}\s+(.+)$/m)?.[1] ?? ""
|
|
22
|
+
const clean = heading.replace(/[*_`]/g, "").trim()
|
|
23
|
+
return clean || "this section"
|
|
24
|
+
})
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<!-- flow-root (inside <Markdown>) contains the Listen control's float so it
|
|
28
|
+
can never overhang into the next block. -->
|
|
29
|
+
<article data-block-markdown>
|
|
30
|
+
<Markdown source={params?.markdown}>
|
|
31
|
+
<SectionTools {ctx} {listenLabel} />
|
|
32
|
+
</Markdown>
|
|
33
|
+
</article>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* markdown block — prose (headings, lists, tables, code, links) through the
|
|
3
|
+
* shared sanitized renderer, drawn by the shared <Markdown> prose surface.
|
|
4
|
+
*
|
|
5
|
+
* The block's own Listen control floats at its top-right (SectionTools)
|
|
6
|
+
* and reads THIS block's paragraphs only — named for the block's first
|
|
7
|
+
* heading, so the button says what it will read.
|
|
8
|
+
*/
|
|
9
|
+
import type { BlockProps } from "../../types.js";
|
|
10
|
+
declare const MarkdownBlock: import("svelte").Component<BlockProps<{
|
|
11
|
+
markdown: string;
|
|
12
|
+
reviewable?: boolean | undefined;
|
|
13
|
+
}>, {}, "">;
|
|
14
|
+
type MarkdownBlock = ReturnType<typeof MarkdownBlock>;
|
|
15
|
+
export default MarkdownBlock;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* markdown block — sanitized prose (headings, lists, tables, code, links).
|
|
3
|
+
*/
|
|
4
|
+
import type { ArtifactBlockDef } from "../../types.js";
|
|
5
|
+
import { type MarkdownParams } from "./schema.js";
|
|
6
|
+
declare const def: ArtifactBlockDef<MarkdownParams>;
|
|
7
|
+
export default def;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import MarkdownBlock from "./MarkdownBlock.svelte";
|
|
2
|
+
import { markdownSchema } from "./schema.js";
|
|
3
|
+
const EXAMPLE = `## Q3 pipeline review
|
|
4
|
+
|
|
5
|
+
Coverage improved across **all three regions**, driven by the renewals push:
|
|
6
|
+
|
|
7
|
+
1. Enterprise: 92%
|
|
8
|
+
2. Mid-market: 84%
|
|
9
|
+
3. SMB: 71%
|
|
10
|
+
|
|
11
|
+
| Region | Coverage | Δ |
|
|
12
|
+
| ------ | -------: | ----: |
|
|
13
|
+
| EMEA | 91.2% | +3.1 |
|
|
14
|
+
| AMER | 88.4% | +2.6 |
|
|
15
|
+
|
|
16
|
+
Run \`npm run audit\` to regenerate the numbers. Full notes in the
|
|
17
|
+
[launch doc](https://example.com/launch).`;
|
|
18
|
+
const def = {
|
|
19
|
+
type: "markdown",
|
|
20
|
+
title: "Markdown",
|
|
21
|
+
description: "Prose written in markdown: headings, lists, tables, fenced code, bold/italic and links render through the shared sanitized renderer (render.ts, exported for other blocks); a bare https:// URL becomes a link. Use it for narrative sections, summaries, instructions and any text you would otherwise hand-write in HTML. Source is rendered as markdown — raw HTML in it is stripped, links must be http(s):// or root-relative and image URLs https:// or root-relative, and the body is capped at 50,000 characters (params budget ~256KB/block). Tip: keep one topic per block so sections can be reordered independently.",
|
|
22
|
+
schema: markdownSchema,
|
|
23
|
+
component: MarkdownBlock,
|
|
24
|
+
// The section's Listen + comment sit on the heading row.
|
|
25
|
+
hostsTools: () => true,
|
|
26
|
+
example: { markdown: EXAMPLE },
|
|
27
|
+
};
|
|
28
|
+
export default def;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Markdown → sanitized HTML for `{@html}`. "" for empty input. */
|
|
2
|
+
export declare function renderBlockMarkdown(markdown: string | null | undefined): string;
|
|
3
|
+
/**
|
|
4
|
+
* Inline-only markdown: bold/emphasis/code/links for short authored
|
|
5
|
+
* strings (labels, option details) — no block structure, so a label can't
|
|
6
|
+
* grow paragraphs or headings. Same sanitize pass and URL policy.
|
|
7
|
+
*/
|
|
8
|
+
export declare function renderBlockMarkdownInline(text: string | null | undefined): string;
|
|
9
|
+
/**
|
|
10
|
+
* Does this text carry OBVIOUS markdown syntax? The gate for "render rich or
|
|
11
|
+
* keep plain" on fields that were historically plain text: plain text must
|
|
12
|
+
* render unchanged — whitespace and line breaks exactly as authored — while
|
|
13
|
+
* authored markdown renders rich. Conservative on purpose: only
|
|
14
|
+
* unambiguous, line-anchored or paired syntax counts. Snake_case stays
|
|
15
|
+
* snake_case; `a - b` mid-line stays a dash.
|
|
16
|
+
*/
|
|
17
|
+
export declare function looksLikeMarkdown(text: string | null | undefined): boolean;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SHARED sanitized markdown renderer for artifact blocks.
|
|
3
|
+
*
|
|
4
|
+
* The blocks contract (../../CONTRACT.md) allows exactly one {@html} path:
|
|
5
|
+
* markdown rendered through THIS function. Any block (markdown itself, the
|
|
6
|
+
* callout body and sections, future blocks with rich prose) imports from
|
|
7
|
+
* here — or renders `<Markdown>` (../../Markdown.svelte), which wraps it
|
|
8
|
+
* with the shared prose styles — never marked/DOMPurify directly.
|
|
9
|
+
*
|
|
10
|
+
* Pipeline (MOS #lib/utils/markdown + blocks/markdown/render.ts, folded
|
|
11
|
+
* into one module for jatui-vsuzi):
|
|
12
|
+
*
|
|
13
|
+
* marked (GFM: tables, fenced code, strikethrough, and AUTOLINKS — a bare
|
|
14
|
+
* `https://…` in prose becomes a link, PARAMS-SPEC §4's renderer floor)
|
|
15
|
+
* → DOMPurify html profile, with one afterSanitizeAttributes hook:
|
|
16
|
+
*
|
|
17
|
+
* - `href` must be a navigation link: `http(s)://…`, root-relative `/…`
|
|
18
|
+
* or `#fragment` (params.ts isSafeLinkUrl); `src` must be a resource:
|
|
19
|
+
* `https://…` or root-relative (isSafeResourceUrl). Anything else
|
|
20
|
+
* (javascript:, data:, protocol-relative //host, bare-relative) has the
|
|
21
|
+
* attribute stripped, so the element renders as inert text.
|
|
22
|
+
* - absolute http(s) anchors get target=_blank + rel=noopener noreferrer
|
|
23
|
+
* (root-relative links stay in-app, no target). Added INSIDE the
|
|
24
|
+
* sanitize pass, so they cannot be re-validated away.
|
|
25
|
+
* - style/iframe/object/embed/form are forbidden outright.
|
|
26
|
+
*
|
|
27
|
+
* SSR-safe: isomorphic-dompurify works in node (publish-time validation,
|
|
28
|
+
* server render) and the browser. The hook is added/removed synchronously
|
|
29
|
+
* around the synchronous sanitize call — hooks are global on the instance.
|
|
30
|
+
*/
|
|
31
|
+
import DOMPurify from "isomorphic-dompurify";
|
|
32
|
+
import { marked } from "marked";
|
|
33
|
+
import { isSafeLinkUrl, isSafeResourceUrl } from "../../params.js";
|
|
34
|
+
function blockSanitizeHook(node) {
|
|
35
|
+
const href = node.getAttribute("href");
|
|
36
|
+
if (href !== null && !isSafeLinkUrl(href.trim()))
|
|
37
|
+
node.removeAttribute("href");
|
|
38
|
+
const src = node.getAttribute("src");
|
|
39
|
+
if (src !== null && !isSafeResourceUrl(src.trim()))
|
|
40
|
+
node.removeAttribute("src");
|
|
41
|
+
if (node.tagName === "A") {
|
|
42
|
+
const kept = node.getAttribute("href") ?? "";
|
|
43
|
+
if (/^https?:\/\//i.test(kept)) {
|
|
44
|
+
node.setAttribute("target", "_blank");
|
|
45
|
+
node.setAttribute("rel", "noopener noreferrer");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Wrap every table in a horizontal scroller so a wide GFM table scrolls
|
|
51
|
+
* inside its block instead of pushing the page wider than a phone. Runs on
|
|
52
|
+
* the SANITIZED output (the wrapper is our own markup, never user input).
|
|
53
|
+
*/
|
|
54
|
+
function wrapTables(html) {
|
|
55
|
+
return html
|
|
56
|
+
.replace(/<table(\s[^>]*)?>/g, (m) => `<div class="md-table" data-md-table>${m}`)
|
|
57
|
+
.replace(/<\/table>/g, "</table></div>");
|
|
58
|
+
}
|
|
59
|
+
/** Markdown → sanitized HTML for `{@html}`. "" for empty input. */
|
|
60
|
+
export function renderBlockMarkdown(markdown) {
|
|
61
|
+
if (typeof markdown !== "string" || !markdown.trim())
|
|
62
|
+
return "";
|
|
63
|
+
const raw = marked.parse(markdown, { async: false, gfm: true });
|
|
64
|
+
DOMPurify.addHook("afterSanitizeAttributes", blockSanitizeHook);
|
|
65
|
+
try {
|
|
66
|
+
return wrapTables(DOMPurify.sanitize(raw, {
|
|
67
|
+
USE_PROFILES: { html: true },
|
|
68
|
+
FORBID_TAGS: ["style", "iframe", "object", "embed", "form"],
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
DOMPurify.removeHook("afterSanitizeAttributes", blockSanitizeHook);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Inline-only markdown: bold/emphasis/code/links for short authored
|
|
77
|
+
* strings (labels, option details) — no block structure, so a label can't
|
|
78
|
+
* grow paragraphs or headings. Same sanitize pass and URL policy.
|
|
79
|
+
*/
|
|
80
|
+
export function renderBlockMarkdownInline(text) {
|
|
81
|
+
if (typeof text !== "string" || !text)
|
|
82
|
+
return "";
|
|
83
|
+
const raw = marked.parseInline(text, { async: false, gfm: true });
|
|
84
|
+
DOMPurify.addHook("afterSanitizeAttributes", blockSanitizeHook);
|
|
85
|
+
try {
|
|
86
|
+
return DOMPurify.sanitize(raw, {
|
|
87
|
+
USE_PROFILES: { html: true },
|
|
88
|
+
FORBID_TAGS: ["style", "iframe", "object", "embed", "form"],
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
DOMPurify.removeHook("afterSanitizeAttributes", blockSanitizeHook);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Does this text carry OBVIOUS markdown syntax? The gate for "render rich or
|
|
97
|
+
* keep plain" on fields that were historically plain text: plain text must
|
|
98
|
+
* render unchanged — whitespace and line breaks exactly as authored — while
|
|
99
|
+
* authored markdown renders rich. Conservative on purpose: only
|
|
100
|
+
* unambiguous, line-anchored or paired syntax counts. Snake_case stays
|
|
101
|
+
* snake_case; `a - b` mid-line stays a dash.
|
|
102
|
+
*/
|
|
103
|
+
export function looksLikeMarkdown(text) {
|
|
104
|
+
if (typeof text !== "string" || !text.trim())
|
|
105
|
+
return false;
|
|
106
|
+
return (/(^|\n)\s*#{1,6}\s+\S/.test(text) || // heading
|
|
107
|
+
/(^|\n)\s*[-*+]\s+\S/.test(text) || // unordered list
|
|
108
|
+
/(^|\n)\s*\d+[.)]\s+\S/.test(text) || // ordered list
|
|
109
|
+
/(^|\n)\s*>\s?\S/.test(text) || // blockquote
|
|
110
|
+
/(^|\n)\s*([-*_])\s*\1\s*\1[-*_\s]*$/.test(text) || // horizontal rule
|
|
111
|
+
/\*\*[^*\n]+\*\*/.test(text) || // bold
|
|
112
|
+
/`[^`\n]+`/.test(text) || // inline code
|
|
113
|
+
/\[[^\]\n]+\]\([^)\n]+\)/.test(text) // link
|
|
114
|
+
);
|
|
115
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Params schema for the markdown block. Imported by block.ts (def),
|
|
3
|
+
* MarkdownBlock.svelte (props typing) and the tests — keeps them cycle-free.
|
|
4
|
+
*/
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
export declare const markdownSchema: z.ZodObject<{
|
|
7
|
+
reviewable: z.ZodOptional<z.ZodBoolean>;
|
|
8
|
+
markdown: z.ZodString;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
export type MarkdownParams = z.infer<typeof markdownSchema>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Params schema for the markdown block. Imported by block.ts (def),
|
|
3
|
+
* MarkdownBlock.svelte (props typing) and the tests — keeps them cycle-free.
|
|
4
|
+
*/
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
/**
|
|
7
|
+
* 50,000 chars ≈ ≤200KB of UTF-8, inside the contract's ~256KB per-block
|
|
8
|
+
* params budget with headroom for the rest of the block object.
|
|
9
|
+
*/
|
|
10
|
+
const MARKDOWN_MAX_CHARS = 50_000;
|
|
11
|
+
export const markdownSchema = z.object({
|
|
12
|
+
/**
|
|
13
|
+
* Opt in/out of the per-block approve / needs-a-pass strip. Markdown is
|
|
14
|
+
* information, so it takes none by default; a page intro states
|
|
15
|
+
* `reviewable: false` explicitly (PARAMS-SPEC §3).
|
|
16
|
+
*/
|
|
17
|
+
reviewable: z.boolean().optional(),
|
|
18
|
+
markdown: z
|
|
19
|
+
.string()
|
|
20
|
+
.min(1, "markdown is required")
|
|
21
|
+
.max(MARKDOWN_MAX_CHARS, `keep markdown under ${MARKDOWN_MAX_CHARS} characters (per-block params budget ~256KB)`)
|
|
22
|
+
.refine((s) => s.trim().length > 0, "markdown is required"),
|
|
23
|
+
});
|