@joewinke/jatui 0.37.0 → 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/package.json +11 -2
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown (jatui-vsuzi) — the ONE rich-prose surface of artifact blocks:
|
|
3
|
+
* source → the shared sanitized renderer (blocks/markdown/render.ts) →
|
|
4
|
+
* {@html}, with the prose styles carried HERE rather than borrowed from a
|
|
5
|
+
* host's typography plugin. A host without `@tailwindcss/typography` (the
|
|
6
|
+
* jat IDE) rendered MOS markdown as flat text with invisible links
|
|
7
|
+
* (PARAMS-SPEC §4); these scoped styles make headings, lists, tables,
|
|
8
|
+
* code and — above all — links visible everywhere.
|
|
9
|
+
*
|
|
10
|
+
* Token-only theming (DaisyUI `--color-*` → jatui radius tokens), light +
|
|
11
|
+
* dark by construction, phone-safe (tables scroll inside the block, long
|
|
12
|
+
* words wrap). Paragraphs and list items cap near 70ch.
|
|
13
|
+
*
|
|
14
|
+
* The {@html} here is the contract's sanctioned path: the HTML has passed
|
|
15
|
+
* marked + DOMPurify with the blocks URL policy.
|
|
16
|
+
*/
|
|
17
|
+
import type { Snippet } from "svelte";
|
|
18
|
+
interface Props {
|
|
19
|
+
source: string | null | undefined;
|
|
20
|
+
/** Inline only: no paragraphs/headings (labels, option details). */
|
|
21
|
+
inline?: boolean;
|
|
22
|
+
/** Smaller body text (card sections, comment threads). */
|
|
23
|
+
size?: "base" | "sm";
|
|
24
|
+
class?: string;
|
|
25
|
+
/** Rendered before the prose (SectionTools floats here). */
|
|
26
|
+
children?: Snippet;
|
|
27
|
+
}
|
|
28
|
+
declare const Markdown: import("svelte").Component<Props, {}, "">;
|
|
29
|
+
type Markdown = ReturnType<typeof Markdown>;
|
|
30
|
+
export default Markdown;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* NoteComposer (jatui-vsuzi) — the ONE composer mount every artifact-block
|
|
4
|
+
* surface uses (verdict notes, comment threads, the Finish note, and the
|
|
5
|
+
* blocks' own per-item notes). It resolves the host's composer from the
|
|
6
|
+
* adapter (HostAdapter.composer — MOS passes its ReviewComposer) and its
|
|
7
|
+
* upload leg (HostAdapter.upload(artifactId)); without either it falls back
|
|
8
|
+
* to jatui's text composer. Blocks import THIS, never a host composer.
|
|
9
|
+
*/
|
|
10
|
+
import { hostAdapter } from "./adapter.js"
|
|
11
|
+
import PlainComposer from "./PlainComposer.svelte"
|
|
12
|
+
import type { NoteComposerProps } from "./types.js"
|
|
13
|
+
|
|
14
|
+
interface Props extends NoteComposerProps {
|
|
15
|
+
/** The artifact the note belongs to — the upload leg's target. */
|
|
16
|
+
artifactId: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let {
|
|
20
|
+
artifactId,
|
|
21
|
+
text = $bindable(""),
|
|
22
|
+
clips = $bindable([]),
|
|
23
|
+
...rest
|
|
24
|
+
}: Props = $props()
|
|
25
|
+
|
|
26
|
+
const adapter = hostAdapter()
|
|
27
|
+
const Composer = adapter.composer ?? PlainComposer
|
|
28
|
+
const upload = $derived(adapter.upload?.(artifactId))
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<Composer bind:text bind:clips {...rest} upload={rest.upload ?? upload} />
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { NoteComposerProps } from "./types.js";
|
|
2
|
+
interface Props extends NoteComposerProps {
|
|
3
|
+
/** The artifact the note belongs to — the upload leg's target. */
|
|
4
|
+
artifactId: string;
|
|
5
|
+
}
|
|
6
|
+
declare const NoteComposer: import("svelte").Component<Props, {}, "text" | "clips">;
|
|
7
|
+
type NoteComposer = ReturnType<typeof NoteComposer>;
|
|
8
|
+
export default NoteComposer;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* PlainComposer (jatui-vsuzi) — the default note composer when the host
|
|
4
|
+
* supplies none (HostAdapter.composer). Text first; with an `upload` leg
|
|
5
|
+
* it also takes files (picker, paste, drop) and shows them as chips. No
|
|
6
|
+
* recording: hosts that want voice/video/screen capture pass their own
|
|
7
|
+
* composer (MOS's ReviewComposer), which takes the same NoteComposerProps.
|
|
8
|
+
*
|
|
9
|
+
* Two modes, like every composer: a submit row (`submitLabel` +
|
|
10
|
+
* `onSubmit`, cleared on success, kept on failure) or host-driven
|
|
11
|
+
* (`bind:text` / `bind:clips`, no submit row).
|
|
12
|
+
*/
|
|
13
|
+
import type { NoteAttachment, NoteComposerProps } from "./types.js"
|
|
14
|
+
import AttachmentChip from "./AttachmentChip.svelte"
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
upload = undefined,
|
|
18
|
+
placeholder = "Type a note…",
|
|
19
|
+
label = "Note",
|
|
20
|
+
maxLength = undefined,
|
|
21
|
+
rows = 2,
|
|
22
|
+
submitLabel = undefined,
|
|
23
|
+
onSubmit = undefined,
|
|
24
|
+
busy = false,
|
|
25
|
+
disabled = false,
|
|
26
|
+
error = null,
|
|
27
|
+
onInput = undefined,
|
|
28
|
+
onBlur = undefined,
|
|
29
|
+
clips = $bindable([]),
|
|
30
|
+
onClipsChange = undefined,
|
|
31
|
+
maxClips = 10,
|
|
32
|
+
autofocus = false,
|
|
33
|
+
text = $bindable(""),
|
|
34
|
+
class: className = "",
|
|
35
|
+
}: NoteComposerProps = $props()
|
|
36
|
+
|
|
37
|
+
let inputEl = $state<HTMLTextAreaElement | null>(null)
|
|
38
|
+
let fileEl = $state<HTMLInputElement | null>(null)
|
|
39
|
+
let uploading = $state(0)
|
|
40
|
+
let localError = $state<string | null>(null)
|
|
41
|
+
let submitting = $state(false)
|
|
42
|
+
|
|
43
|
+
$effect(() => {
|
|
44
|
+
if (autofocus && inputEl) inputEl.focus()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const canAttach = $derived(Boolean(upload) && !disabled)
|
|
48
|
+
const shown = $derived(error ?? localError)
|
|
49
|
+
|
|
50
|
+
function setClips(next: NoteAttachment[]) {
|
|
51
|
+
clips = next
|
|
52
|
+
onClipsChange?.(next)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function addFiles(files: Iterable<File>) {
|
|
56
|
+
if (!upload) return
|
|
57
|
+
for (const f of files) {
|
|
58
|
+
if ((clips?.length ?? 0) + uploading >= maxClips) {
|
|
59
|
+
localError = `Up to ${maxClips} attachments per note.`
|
|
60
|
+
break
|
|
61
|
+
}
|
|
62
|
+
uploading++
|
|
63
|
+
localError = null
|
|
64
|
+
try {
|
|
65
|
+
const clip = await upload(f, "file")
|
|
66
|
+
setClips([...(clips ?? []), clip])
|
|
67
|
+
} catch (e) {
|
|
68
|
+
localError = e instanceof Error ? e.message : "Couldn't attach that file."
|
|
69
|
+
} finally {
|
|
70
|
+
uploading--
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function onPaste(e: ClipboardEvent) {
|
|
76
|
+
if (!canAttach) return
|
|
77
|
+
const files = [...(e.clipboardData?.files ?? [])]
|
|
78
|
+
if (files.length === 0) return
|
|
79
|
+
e.preventDefault()
|
|
80
|
+
void addFiles(files)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function onDrop(e: DragEvent) {
|
|
84
|
+
if (!canAttach) return
|
|
85
|
+
const files = [...(e.dataTransfer?.files ?? [])]
|
|
86
|
+
if (files.length === 0) return
|
|
87
|
+
e.preventDefault()
|
|
88
|
+
void addFiles(files)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function submit() {
|
|
92
|
+
if (!onSubmit || submitting || busy) return
|
|
93
|
+
const body = text.trim()
|
|
94
|
+
if (!body && (clips?.length ?? 0) === 0) return
|
|
95
|
+
submitting = true
|
|
96
|
+
localError = null
|
|
97
|
+
try {
|
|
98
|
+
await onSubmit({ text: body, clips: clips ?? [] })
|
|
99
|
+
text = ""
|
|
100
|
+
setClips([])
|
|
101
|
+
} catch (e) {
|
|
102
|
+
// The draft stays; the host's `error` (or this) says why.
|
|
103
|
+
if (!error)
|
|
104
|
+
localError = e instanceof Error ? e.message : "Could not send that."
|
|
105
|
+
} finally {
|
|
106
|
+
submitting = false
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function onKeydown(e: KeyboardEvent) {
|
|
111
|
+
if (submitLabel && e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
112
|
+
e.preventDefault()
|
|
113
|
+
void submit()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<div
|
|
119
|
+
class="flex flex-col gap-2 {className}"
|
|
120
|
+
ondragover={(e) => canAttach && e.preventDefault()}
|
|
121
|
+
ondrop={onDrop}
|
|
122
|
+
role="group"
|
|
123
|
+
aria-label={label}
|
|
124
|
+
data-plain-composer
|
|
125
|
+
>
|
|
126
|
+
<textarea
|
|
127
|
+
bind:this={inputEl}
|
|
128
|
+
bind:value={text}
|
|
129
|
+
class="textarea textarea-sm w-full rounded-field"
|
|
130
|
+
{rows}
|
|
131
|
+
maxlength={maxLength}
|
|
132
|
+
{placeholder}
|
|
133
|
+
aria-label={label}
|
|
134
|
+
disabled={disabled || busy || submitting}
|
|
135
|
+
oninput={() => onInput?.(text)}
|
|
136
|
+
onblur={() => onBlur?.()}
|
|
137
|
+
onpaste={onPaste}
|
|
138
|
+
onkeydown={onKeydown}
|
|
139
|
+
></textarea>
|
|
140
|
+
{#if (clips?.length ?? 0) > 0 || uploading > 0}
|
|
141
|
+
<ul class="flex flex-wrap items-center gap-1.5" aria-label="Attachments">
|
|
142
|
+
{#each clips ?? [] as clip (clip.name)}
|
|
143
|
+
<li class="flex items-center gap-0.5">
|
|
144
|
+
<AttachmentChip {clip} />
|
|
145
|
+
{#if !disabled}
|
|
146
|
+
<button
|
|
147
|
+
type="button"
|
|
148
|
+
class="btn btn-ghost btn-xs btn-circle"
|
|
149
|
+
aria-label="Remove {clip.label ?? clip.name}"
|
|
150
|
+
onclick={() => setClips((clips ?? []).filter((c) => c !== clip))}
|
|
151
|
+
>✕</button
|
|
152
|
+
>
|
|
153
|
+
{/if}
|
|
154
|
+
</li>
|
|
155
|
+
{/each}
|
|
156
|
+
{#if uploading > 0}
|
|
157
|
+
<li class="text-base-content/60 flex items-center gap-1.5 text-xs">
|
|
158
|
+
<span class="loading loading-spinner loading-xs"></span>Attaching…
|
|
159
|
+
</li>
|
|
160
|
+
{/if}
|
|
161
|
+
</ul>
|
|
162
|
+
{/if}
|
|
163
|
+
{#if shown}
|
|
164
|
+
<p role="alert" class="text-error text-xs">{shown}</p>
|
|
165
|
+
{/if}
|
|
166
|
+
{#if canAttach || submitLabel}
|
|
167
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
168
|
+
{#if canAttach}
|
|
169
|
+
<input
|
|
170
|
+
bind:this={fileEl}
|
|
171
|
+
type="file"
|
|
172
|
+
multiple
|
|
173
|
+
class="hidden"
|
|
174
|
+
onchange={(e) => {
|
|
175
|
+
const input = e.currentTarget as HTMLInputElement
|
|
176
|
+
void addFiles([...(input.files ?? [])])
|
|
177
|
+
input.value = ""
|
|
178
|
+
}}
|
|
179
|
+
/>
|
|
180
|
+
<button
|
|
181
|
+
type="button"
|
|
182
|
+
class="btn btn-ghost btn-xs rounded-field"
|
|
183
|
+
onclick={() => fileEl?.click()}
|
|
184
|
+
>
|
|
185
|
+
Attach file
|
|
186
|
+
</button>
|
|
187
|
+
{/if}
|
|
188
|
+
{#if submitLabel}
|
|
189
|
+
<button
|
|
190
|
+
type="button"
|
|
191
|
+
class="btn btn-primary btn-sm rounded-field ml-auto"
|
|
192
|
+
disabled={disabled || busy || submitting || uploading > 0}
|
|
193
|
+
onclick={submit}
|
|
194
|
+
>
|
|
195
|
+
{busy || submitting ? "Sending…" : submitLabel}
|
|
196
|
+
</button>
|
|
197
|
+
{/if}
|
|
198
|
+
</div>
|
|
199
|
+
{/if}
|
|
200
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PlainComposer (jatui-vsuzi) — the default note composer when the host
|
|
3
|
+
* supplies none (HostAdapter.composer). Text first; with an `upload` leg
|
|
4
|
+
* it also takes files (picker, paste, drop) and shows them as chips. No
|
|
5
|
+
* recording: hosts that want voice/video/screen capture pass their own
|
|
6
|
+
* composer (MOS's ReviewComposer), which takes the same NoteComposerProps.
|
|
7
|
+
*
|
|
8
|
+
* Two modes, like every composer: a submit row (`submitLabel` +
|
|
9
|
+
* `onSubmit`, cleared on success, kept on failure) or host-driven
|
|
10
|
+
* (`bind:text` / `bind:clips`, no submit row).
|
|
11
|
+
*/
|
|
12
|
+
import type { NoteComposerProps } from "./types.js";
|
|
13
|
+
declare const PlainComposer: import("svelte").Component<NoteComposerProps, {}, "text" | "clips">;
|
|
14
|
+
type PlainComposer = ReturnType<typeof PlainComposer>;
|
|
15
|
+
export default PlainComposer;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* SectionTools (origin mos-8fvt9) — a section's own controls on its
|
|
4
|
+
* heading row: the block's Listen pill (when it reads prose) and its
|
|
5
|
+
* comment pill, right-aligned and compact, ON the heading's line rather
|
|
6
|
+
* than stacked above it.
|
|
7
|
+
*
|
|
8
|
+
* Placement is a right float the heading (and the text after it) wraps
|
|
9
|
+
* around, so a long heading never collides with the controls on a phone.
|
|
10
|
+
* The host wraps it in a `flow-root` box so the float never overhangs
|
|
11
|
+
* into the next block. When a Listen player opens, its bar replaces the
|
|
12
|
+
* pill and the group takes the full width — the transport lives with the
|
|
13
|
+
* text it reads.
|
|
14
|
+
*
|
|
15
|
+
* Listen comes from the host's Listen snippet (HostAdapter.listen, or a
|
|
16
|
+
* provideBlockListen() override below it; none on a share link → no
|
|
17
|
+
* Listen); the comment pill is `ctx.tools` from BlockRenderer (none
|
|
18
|
+
* without comments → nothing). With neither, nothing renders at all.
|
|
19
|
+
*/
|
|
20
|
+
import { hostAdapter } from "./adapter.js"
|
|
21
|
+
import { blockListen } from "./listen/context.js"
|
|
22
|
+
import type { BlockContext } from "./types.js"
|
|
23
|
+
|
|
24
|
+
interface Props {
|
|
25
|
+
ctx: BlockContext | undefined
|
|
26
|
+
/**
|
|
27
|
+
* Human name of what this block's Listen reads ("What this page is"),
|
|
28
|
+
* completing "Listen to: <label>". Omitted = no Listen (a block that
|
|
29
|
+
* is not prose: a stats grid, a table).
|
|
30
|
+
*/
|
|
31
|
+
listenLabel?: string | null
|
|
32
|
+
/**
|
|
33
|
+
* A flex item in the block's own title row instead of a float (blocks
|
|
34
|
+
* whose title is a short line above a grid, table or media: a float
|
|
35
|
+
* there would narrow the grid beside it).
|
|
36
|
+
*/
|
|
37
|
+
inline?: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let { ctx, listenLabel = null, inline = false }: Props = $props()
|
|
41
|
+
|
|
42
|
+
const override = blockListen()
|
|
43
|
+
const adapter = hostAdapter()
|
|
44
|
+
const listen = $derived(
|
|
45
|
+
listenLabel !== null ? (override ? override.render : (adapter.listen ?? null)) : null,
|
|
46
|
+
)
|
|
47
|
+
const group = $derived(`block-${ctx?.blockId ?? ""}`)
|
|
48
|
+
|
|
49
|
+
/** The block's own section is the highlight/scroll-follow target: the
|
|
50
|
+
* group IS BlockRenderer's `<section id>`. */
|
|
51
|
+
function anchorFor(g: string): string {
|
|
52
|
+
return typeof CSS !== "undefined" && CSS.escape ? `#${CSS.escape(g)}` : `#${g}`
|
|
53
|
+
}
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
{#if listen || ctx?.tools}
|
|
57
|
+
<div class="section-tools" class:inline data-section-tools>
|
|
58
|
+
{#if listen}
|
|
59
|
+
{@render listen({ group, label: listenLabel ?? "", anchor: anchorFor(group) })}
|
|
60
|
+
{/if}
|
|
61
|
+
{#if ctx?.tools}
|
|
62
|
+
{@render ctx.tools()}
|
|
63
|
+
{/if}
|
|
64
|
+
</div>
|
|
65
|
+
{/if}
|
|
66
|
+
|
|
67
|
+
<style>
|
|
68
|
+
/* Float at the heading's right; the heading wraps around it. The element
|
|
69
|
+
right after the float drops its top margin (a prose h2 carries 2em),
|
|
70
|
+
so the heading's first line sits level with the controls instead of
|
|
71
|
+
a heading-margin below them. */
|
|
72
|
+
.section-tools {
|
|
73
|
+
float: right;
|
|
74
|
+
display: flex;
|
|
75
|
+
align-items: center;
|
|
76
|
+
gap: 0.375rem;
|
|
77
|
+
margin: 0 0 0.25rem 0.75rem;
|
|
78
|
+
}
|
|
79
|
+
.section-tools + :global(*) {
|
|
80
|
+
margin-top: 0 !important;
|
|
81
|
+
}
|
|
82
|
+
.section-tools.inline {
|
|
83
|
+
float: none;
|
|
84
|
+
flex-shrink: 0;
|
|
85
|
+
margin: 0 0 0 auto;
|
|
86
|
+
}
|
|
87
|
+
/* Open: the bar takes the full width, the comment pill stays beside it.
|
|
88
|
+
`.lc-bar` is jatui ListenControl's open bar; a host player can opt in
|
|
89
|
+
with `data-listen-open`. */
|
|
90
|
+
.section-tools:has(:global(.lc-bar)),
|
|
91
|
+
.section-tools:has(:global([data-listen-open])) {
|
|
92
|
+
float: none;
|
|
93
|
+
clear: both;
|
|
94
|
+
width: 100%;
|
|
95
|
+
margin: 0 0 0.5rem 0;
|
|
96
|
+
}
|
|
97
|
+
.section-tools:has(:global(.lc-bar)) > :global(:first-child),
|
|
98
|
+
.section-tools:has(:global([data-listen-open])) > :global(:first-child) {
|
|
99
|
+
flex: 1 1 auto;
|
|
100
|
+
min-width: 0;
|
|
101
|
+
}
|
|
102
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BlockContext } from "./types.js";
|
|
2
|
+
interface Props {
|
|
3
|
+
ctx: BlockContext | undefined;
|
|
4
|
+
/**
|
|
5
|
+
* Human name of what this block's Listen reads ("What this page is"),
|
|
6
|
+
* completing "Listen to: <label>". Omitted = no Listen (a block that
|
|
7
|
+
* is not prose: a stats grid, a table).
|
|
8
|
+
*/
|
|
9
|
+
listenLabel?: string | null;
|
|
10
|
+
/**
|
|
11
|
+
* A flex item in the block's own title row instead of a float (blocks
|
|
12
|
+
* whose title is a short line above a grid, table or media: a float
|
|
13
|
+
* there would narrow the grid beside it).
|
|
14
|
+
*/
|
|
15
|
+
inline?: boolean;
|
|
16
|
+
}
|
|
17
|
+
declare const SectionTools: import("svelte").Component<Props, {}, "">;
|
|
18
|
+
type SectionTools = ReturnType<typeof SectionTools>;
|
|
19
|
+
export default SectionTools;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BlockServices, HostAdapter } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The context key itself, for tests that mount a block or a chrome piece
|
|
4
|
+
* directly (`mount(C, { context: new Map([[HOST_ADAPTER_KEY, adapter]]) })`).
|
|
5
|
+
* Hosts call provideHostAdapter() (or pass `adapter` to the view).
|
|
6
|
+
*/
|
|
7
|
+
export declare const HOST_ADAPTER_KEY: symbol;
|
|
8
|
+
/** Host-side: publish the adapter to every component below. */
|
|
9
|
+
export declare function provideHostAdapter(adapter: HostAdapter | null | undefined): void;
|
|
10
|
+
/** Component-side: the adapter, or the empty (read-only) one. */
|
|
11
|
+
export declare function hostAdapter(): HostAdapter;
|
|
12
|
+
/** The block services (always an object). */
|
|
13
|
+
export declare function hostServices(adapter: HostAdapter): BlockServices;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HostAdapter on the Svelte context chain (jatui-vsuzi).
|
|
3
|
+
*
|
|
4
|
+
* BlocksArtifactView (or a bare BlockRenderer) publishes the host's adapter
|
|
5
|
+
* once; every component below — BlockVerdict, BlockComments, NoteComposer,
|
|
6
|
+
* SectionTools and any block — reads it from here instead of importing a
|
|
7
|
+
* host module. No adapter → the read-only defaults: no comments, no review
|
|
8
|
+
* send, text-only notes, no Listen, no history, no live-data refresh.
|
|
9
|
+
*/
|
|
10
|
+
import { getContext, setContext } from "svelte";
|
|
11
|
+
const KEY = Symbol("artifact-blocks-host-adapter");
|
|
12
|
+
/**
|
|
13
|
+
* The context key itself, for tests that mount a block or a chrome piece
|
|
14
|
+
* directly (`mount(C, { context: new Map([[HOST_ADAPTER_KEY, adapter]]) })`).
|
|
15
|
+
* Hosts call provideHostAdapter() (or pass `adapter` to the view).
|
|
16
|
+
*/
|
|
17
|
+
export const HOST_ADAPTER_KEY = KEY;
|
|
18
|
+
const EMPTY = Object.freeze({});
|
|
19
|
+
const NO_SERVICES = Object.freeze({});
|
|
20
|
+
/** Host-side: publish the adapter to every component below. */
|
|
21
|
+
export function provideHostAdapter(adapter) {
|
|
22
|
+
setContext(KEY, adapter ?? EMPTY);
|
|
23
|
+
}
|
|
24
|
+
/** Component-side: the adapter, or the empty (read-only) one. */
|
|
25
|
+
export function hostAdapter() {
|
|
26
|
+
return getContext(KEY) ?? EMPTY;
|
|
27
|
+
}
|
|
28
|
+
/** The block services (always an object). */
|
|
29
|
+
export function hostServices(adapter) {
|
|
30
|
+
return adapter.services ?? NO_SERVICES;
|
|
31
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-send when a review is complete (mos-yra79, Joe 2026-10-19: "after I
|
|
3
|
+
* do it in the page I have to come back here and click Send? Why didn't
|
|
4
|
+
* they send at the time?").
|
|
5
|
+
*
|
|
6
|
+
* The moment the viewer decides the LAST open item on a page, the page
|
|
7
|
+
* sends by itself after a short grace: "All decided · sending in 5s ·
|
|
8
|
+
* Undo". Undo keeps the page a draft. The send is the page's own Send
|
|
9
|
+
* (Finish review / Done reviewing): no new semantics.
|
|
10
|
+
*
|
|
11
|
+
* The rule, as a state machine fed by the page on every change:
|
|
12
|
+
*
|
|
13
|
+
* - it arms only on a TRANSITION to complete seen in this tab. A page
|
|
14
|
+
* that loads already complete (an old unsent draft) never sends by
|
|
15
|
+
* itself; the desk's "Send all" is for those;
|
|
16
|
+
* - any open item again (an undecided verdict) cancels, and re-arms on
|
|
17
|
+
* the next completion;
|
|
18
|
+
* - Undo cancels and stays cancelled until the page is incomplete again,
|
|
19
|
+
* so a viewer who said "not yet" is not asked again by the next save;
|
|
20
|
+
* - typing (`bump`) restarts the grace, so a note the viewer is still
|
|
21
|
+
* writing is never cut off mid-word. The send flushes it first.
|
|
22
|
+
*
|
|
23
|
+
* Runes module: each call creates its own state capsule.
|
|
24
|
+
*/
|
|
25
|
+
export interface AutoSend {
|
|
26
|
+
/** A send is counting down. */
|
|
27
|
+
readonly pending: boolean;
|
|
28
|
+
/** Whole seconds left on the countdown (0 when not pending). */
|
|
29
|
+
readonly remaining: number;
|
|
30
|
+
/** Feed the page's state: every unit decided, and a send is possible. */
|
|
31
|
+
update: (complete: boolean, eligible: boolean) => void;
|
|
32
|
+
/** Cancel the countdown and keep the page a draft. */
|
|
33
|
+
undo: () => void;
|
|
34
|
+
/** Send now, skipping the rest of the grace. */
|
|
35
|
+
sendNow: () => void;
|
|
36
|
+
/** The viewer is still typing: restart the grace. */
|
|
37
|
+
bump: () => void;
|
|
38
|
+
/** Tear down timers (unmount). */
|
|
39
|
+
stop: () => void;
|
|
40
|
+
}
|
|
41
|
+
interface AutoSendOptions {
|
|
42
|
+
/** The grace before the send, in ms. */
|
|
43
|
+
delayMs?: number;
|
|
44
|
+
/** The page's own send. */
|
|
45
|
+
onSend: () => void | Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
export declare const AUTO_SEND_DELAY_MS = 5000;
|
|
48
|
+
export declare function createAutoSend(opts: AutoSendOptions): AutoSend;
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* autoSend.svelte.js generated by Svelte v5.53.11 */
|
|
2
|
+
import * as $ from 'svelte/internal/client';
|
|
3
|
+
|
|
4
|
+
export const AUTO_SEND_DELAY_MS = 5_000;
|
|
5
|
+
|
|
6
|
+
export function createAutoSend(opts) {
|
|
7
|
+
const delay = opts.delayMs ?? AUTO_SEND_DELAY_MS;
|
|
8
|
+
let pending = $.state(false);
|
|
9
|
+
let remaining = $.state(0);
|
|
10
|
+
|
|
11
|
+
/** The last state seen; null before the first update (the baseline). */
|
|
12
|
+
let wasComplete = null;
|
|
13
|
+
|
|
14
|
+
/** Undo was pressed on this completion. */
|
|
15
|
+
let dismissed = false;
|
|
16
|
+
|
|
17
|
+
let deadline = 0;
|
|
18
|
+
let fireTimer = null;
|
|
19
|
+
let tickTimer = null;
|
|
20
|
+
|
|
21
|
+
function clearTimers() {
|
|
22
|
+
if (fireTimer !== null) clearTimeout(fireTimer);
|
|
23
|
+
if (tickTimer !== null) clearInterval(tickTimer);
|
|
24
|
+
|
|
25
|
+
fireTimer = null;
|
|
26
|
+
tickTimer = null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function cancel() {
|
|
30
|
+
clearTimers();
|
|
31
|
+
$.set(pending, false);
|
|
32
|
+
$.set(remaining, 0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function fire() {
|
|
36
|
+
cancel();
|
|
37
|
+
void opts.onSend();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function arm() {
|
|
41
|
+
clearTimers();
|
|
42
|
+
$.set(pending, true);
|
|
43
|
+
deadline = Date.now() + delay;
|
|
44
|
+
$.set(remaining, Math.ceil(delay / 1000), true);
|
|
45
|
+
fireTimer = setTimeout(fire, delay);
|
|
46
|
+
|
|
47
|
+
tickTimer = setInterval(
|
|
48
|
+
() => {
|
|
49
|
+
$.set(remaining, Math.max(0, Math.ceil((deadline - Date.now()) / 1000)), true);
|
|
50
|
+
},
|
|
51
|
+
250
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
get pending() {
|
|
57
|
+
return $.get(pending);
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
get remaining() {
|
|
61
|
+
return $.get(remaining);
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
update(complete, eligible) {
|
|
65
|
+
const prev = wasComplete;
|
|
66
|
+
|
|
67
|
+
wasComplete = complete;
|
|
68
|
+
|
|
69
|
+
if (!complete) {
|
|
70
|
+
dismissed = false;
|
|
71
|
+
|
|
72
|
+
if ($.get(pending)) cancel();
|
|
73
|
+
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!eligible) {
|
|
78
|
+
if ($.get(pending)) cancel();
|
|
79
|
+
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (prev === false && !$.get(pending) && !dismissed) arm();
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
undo() {
|
|
87
|
+
dismissed = true;
|
|
88
|
+
cancel();
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
sendNow() {
|
|
92
|
+
if ($.get(pending)) fire();
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
bump() {
|
|
96
|
+
if ($.get(pending)) arm();
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
stop() {
|
|
100
|
+
clearTimers();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|