@fabrika/canvas-kernel 0.1.45 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +33 -67
  2. package/index.ts +20 -83
  3. package/package.json +69 -72
  4. package/src/client/AnnotationProvider.tsx +218 -255
  5. package/src/client/AnnotationSidebar.tsx +393 -338
  6. package/src/client/FileIcon.tsx +105 -66
  7. package/src/client/PlanRenderer.tsx +607 -551
  8. package/src/client/RenderErrorContext.ts +7 -7
  9. package/src/client/ResponsePreview.tsx +192 -173
  10. package/src/client/annotationDom.ts +43 -97
  11. package/src/client/annotationDraft.ts +106 -96
  12. package/src/client/appContext.tsx +40 -40
  13. package/src/client/components/Callout.tsx +20 -20
  14. package/src/client/components/Checklist.tsx +27 -29
  15. package/src/client/components/Choice.tsx +178 -159
  16. package/src/client/components/CodeBlock.tsx +27 -26
  17. package/src/client/components/Diff.tsx +16 -17
  18. package/src/client/components/FilePreview.tsx +102 -74
  19. package/src/client/components/Image.tsx +41 -39
  20. package/src/client/components/Markdown.tsx +33 -32
  21. package/src/client/components/Mermaid.tsx +593 -542
  22. package/src/client/components/Note.tsx +8 -8
  23. package/src/client/components/Priority.tsx +10 -12
  24. package/src/client/components/ResponseNote.tsx +16 -15
  25. package/src/client/components/Section.tsx +28 -26
  26. package/src/client/components/Table.tsx +33 -30
  27. package/src/client/components/Task.tsx +58 -60
  28. package/src/client/components/UserInput.tsx +169 -152
  29. package/src/client/components/index.ts +16 -16
  30. package/src/client/components/renderMarkdownChildren.tsx +86 -68
  31. package/src/client/diffLines.ts +51 -39
  32. package/src/client/generateMarkdown.ts +197 -191
  33. package/src/client/hostContext.tsx +39 -34
  34. package/src/client/index.ts +44 -125
  35. package/src/client/pageGlobals.ts +25 -0
  36. package/src/client/persistenceSchemas.ts +41 -0
  37. package/src/client/preact-all.ts +4 -4
  38. package/src/client/runtime.ts +58 -111
  39. package/src/client/styles.css +1 -74
  40. package/src/engine/compiler.ts +411 -323
  41. package/src/engine/file-admission.ts +25 -0
  42. package/src/engine/paths.ts +44 -44
  43. package/src/engine/session.ts +515 -626
  44. package/src/engine/sessionSchemas.ts +71 -0
  45. package/src/engine/watcher.ts +70 -60
  46. package/src/engine/websocket.ts +134 -132
  47. package/annotate.ts +0 -94
  48. package/daemon.ts +0 -46
  49. package/src/client/AnnotationDraftFooter.tsx +0 -76
  50. package/src/client/AnnotationEditor.tsx +0 -320
  51. package/src/client/AnnotationList.tsx +0 -190
  52. package/src/client/Popover.tsx +0 -361
  53. package/src/client/SessionContext.tsx +0 -3
  54. package/src/client/annotationContext.ts +0 -130
  55. package/src/client/annotationMarkdown.ts +0 -102
  56. package/src/client/annotationTarget.ts +0 -73
  57. package/src/client/blockTarget.ts +0 -127
  58. package/src/client/highlightRange.ts +0 -262
  59. package/src/client/popoverPosition.ts +0 -46
  60. package/src/client/regionTarget.ts +0 -180
  61. package/src/client/testing/dom.ts +0 -74
  62. package/src/client/textTarget.ts +0 -31
  63. package/src/client/useRegionAnnotation.tsx +0 -188
  64. package/src/client/useTextAnnotation.tsx +0 -194
  65. package/src/client/utils.ts +0 -33
  66. package/src/engine/channel.ts +0 -278
  67. package/src/engine/http-utils.ts +0 -14
  68. package/src/engine/lifecycle.ts +0 -302
  69. package/src/engine/router.ts +0 -16
  70. package/src/engine/waiter.ts +0 -188
@@ -1,128 +1,47 @@
1
- // @fabrika/canvas-kernel/client — the reusable annotation surface.
2
- //
3
- // Everything a host needs to render a compiled canvas and collect feedback on
4
- // it, minus the app shell (navigation, file browser, revision chrome), which
5
- // each host builds itself.
6
-
7
- // --- Annotation state ------------------------------------------------------
8
- // Everything that lives in runtime.ts is re-exported through `#canvas/runtime`,
9
- // never through a relative path. The runtime holds the React contexts and ships
10
- // as its own bundle shared with the compiled canvas; importing it relatively
11
- // would bundle a second copy, and the provider and its consumers would then be
12
- // writing to and reading from different contexts.
13
- export { AnnotationProvider } from "./AnnotationProvider";
14
- export type { PersistedState } from "./AnnotationProvider";
1
+ export { AnnotationProvider } from './AnnotationProvider'
2
+ export type { PersistedState } from './AnnotationProvider'
3
+ export { feedbackEntrySchema, parsePersistedState, persistedStateSchema, planResponseSchema } from './persistenceSchemas'
4
+ // Canvas documents and the shell must read the same external context instance.
15
5
  export {
16
- useAnnotations,
17
- useFeedback,
18
- useCanvasFile,
19
- useResponseRegistration,
20
- AnnotationCtx,
21
- SessionContext,
22
- ActiveViewCtx,
23
- CanvasFileCtx,
24
- } from "#canvas/runtime";
6
+ ActiveViewCtx,
7
+ AnnotationCtx,
8
+ CanvasFileCtx,
9
+ SessionContext,
10
+ useAnnotations,
11
+ useCanvasFile,
12
+ useFeedback,
13
+ useResponseRegistration,
14
+ } from '#canvas/runtime'
25
15
  export type {
26
- Annotation,
27
- AnnotationAttachment,
28
- AnnotationAuthor,
29
- AnnotationContext,
30
- AnnotationContextValue,
31
- ActiveView,
32
- FeedbackEntry,
33
- PlanResponse,
34
- } from "#canvas/runtime";
35
-
36
- // --- Surface ---------------------------------------------------------------
37
- export { AnnotationSidebar } from "./AnnotationSidebar";
38
- // The sidebar's reusable halves. Grouping stayed with the sidebar — what an
39
- // annotation belongs to is the host's taxonomy, so the host slices the list.
40
- export { AnnotationList } from "./AnnotationList";
41
- export type { AnnotationListProps } from "./AnnotationList";
42
- export { AnnotationDraftFooter } from "./AnnotationDraftFooter";
43
- export type { AnnotationDraftFooterProps } from "./AnnotationDraftFooter";
44
- export { AnnotationEditor, imageToUrl } from "./AnnotationEditor";
45
- export { PlanRenderer } from "./PlanRenderer";
46
- export { Popover, AnnotationPopover, AnnotationCreatePopover, AnnotationEditPopover } from "./Popover";
47
- export { ResponsePreview, MarkdownPreview } from "./ResponsePreview";
48
- export { FileIcon } from "./FileIcon";
49
-
50
- /** The kernel component set, as a namespace for hosts that inject it wholesale. */
51
- export * as components from "./components";
52
- export { useTextAnnotation } from "./useTextAnnotation";
53
- export { useRegionAnnotation } from "./useRegionAnnotation";
54
-
55
- // --- Host-provided context -------------------------------------------------
56
- export { CanvasHostContext, localCanvasHost, useCanvasHost } from "./hostContext";
57
- export type { CanvasHost } from "./hostContext";
58
- export { ActiveViewContext, RevisionContext } from "./appContext";
59
- export type { CanvasFileInfo, RevisionInfo } from "./appContext";
60
- export { RenderErrorContext } from "./RenderErrorContext";
61
- export type { CanvasRenderError } from "./RenderErrorContext";
62
-
63
- // --- Annotation plumbing ---------------------------------------------------
64
- export { extractContext, formatSnippetInContext } from "./annotationContext";
16
+ ActiveView,
17
+ Annotation,
18
+ AnnotationAttachment,
19
+ AnnotationAuthor,
20
+ AnnotationContext,
21
+ AnnotationContextValue,
22
+ FeedbackEntry,
23
+ PlanResponse,
24
+ } from '#canvas/runtime'
25
+ export { findAnnotationElement, scrollToAnnotation } from './annotationDom'
26
+ export { annotationDraftKey, carryUnsubmittedDraft, clearPersistedDraft } from './annotationDraft'
27
+ export type { AnnotationDraftPhase } from './annotationDraft'
28
+ export { AnnotationSidebar } from './AnnotationSidebar'
29
+ export { ActiveViewContext, RevisionContext } from './appContext'
30
+ export type { CanvasFileInfo, RevisionInfo } from './appContext'
31
+ export * as components from './components'
32
+ export { FileIcon } from './FileIcon'
65
33
  export {
66
- ANNOTATION_TARGETS,
67
- ANNOTATABLE_SELECTOR,
68
- BLOCK_SELECTOR,
69
- describeSnippet,
70
- findAnnotationElement,
71
- findSnippetElement,
72
- getBlockSnippet,
73
- restoreAnnotationTargets,
74
- scrollToAnnotation,
75
- } from "./annotationDom";
76
-
77
- // --- Locator strategies ----------------------------------------------------
78
- // What a snippet points at is the one thing in an annotation that is domain
79
- // specific. A host that needs to annotate something the kernel has no target
80
- // for writes one of these; nothing else has to change.
81
- export { sealTarget } from "./annotationTarget";
82
- export type { AnnotationTarget, SealedAnnotationTarget, TargetAnnotation } from "./annotationTarget";
83
- export { blockTarget } from "./blockTarget";
84
- export type { BlockLocator } from "./blockTarget";
85
- export { textTarget } from "./textTarget";
86
- export type { TextLocator } from "./textTarget";
87
- export {
88
- createRegionOverlay,
89
- describeRegion,
90
- findRegionHost,
91
- isDrawableRegion,
92
- regionBetween,
93
- regionPointIn,
94
- regionStyle,
95
- regionTarget,
96
- REGION_HOST_ATTR,
97
- REGION_UNITS,
98
- } from "./regionTarget";
99
- export type { RegionBox, RegionLocator, RegionPoint, RegionShape } from "./regionTarget";
100
- export {
101
- annotationDraftKey,
102
- carryUnsubmittedDraft,
103
- clearPersistedDraft,
104
- } from "./annotationDraft";
105
- export type { AnnotationDraftPhase } from "./annotationDraft";
106
- export {
107
- restoreMarks,
108
- renameMarkId,
109
- setMarkActive,
110
- unwrapMarks,
111
- updateAllMarkStates,
112
- wrapRangeWithMark,
113
- } from "./highlightRange";
114
- // One annotation as markdown — the half of feedback that belongs to no
115
- // particular document format. `generateMarkdown` below is the canvas format
116
- // built on it; a host with its own headings calls this directly.
117
- export { renderAnnotation } from "./annotationMarkdown";
118
- export type { RenderableAnnotation } from "./annotationMarkdown";
119
- export {
120
- canPruneResponses,
121
- generateMarkdown,
122
- getMissingRequired,
123
- getMissingRequiredFeedback,
124
- getMissingRequiredLabels,
125
- hasValue,
126
- pruneStaleResponses,
127
- } from "./generateMarkdown";
128
- export { autoResizeTextarea, fileAnnotationPath, generateAnnotationId, hostAcceptsUploads, RESPONSE_ANNOTATION_PATH } from "./utils";
34
+ canPruneResponses,
35
+ generateMarkdown,
36
+ getMissingRequired,
37
+ getMissingRequiredFeedback,
38
+ getMissingRequiredLabels,
39
+ hasValue,
40
+ pruneStaleResponses,
41
+ } from './generateMarkdown'
42
+ export { CanvasHostContext, localCanvasHost, useCanvasHost } from './hostContext'
43
+ export type { CanvasHost } from './hostContext'
44
+ export { PlanRenderer } from './PlanRenderer'
45
+ export { RenderErrorContext } from './RenderErrorContext'
46
+ export type { CanvasRenderError } from './RenderErrorContext'
47
+ export { MarkdownPreview, ResponsePreview } from './ResponsePreview'
@@ -0,0 +1,25 @@
1
+ // highlight.js and mermaid reach the page as <script> tags from the host, not through the bundle.
2
+
3
+ interface HighlightJs {
4
+ highlightElement(element: HTMLElement): void
5
+ }
6
+
7
+ interface MermaidApi {
8
+ initialize(config: { startOnLoad: boolean; theme: string }): void
9
+ render(id: string, source: string): Promise<{ svg: string }>
10
+ }
11
+
12
+ declare global {
13
+ interface Window {
14
+ hljs?: HighlightJs
15
+ mermaid?: MermaidApi
16
+ }
17
+ }
18
+
19
+ export function pageHighlighter(): HighlightJs | undefined {
20
+ return window.hljs
21
+ }
22
+
23
+ export function pageMermaid(): MermaidApi | undefined {
24
+ return window.mermaid
25
+ }
@@ -0,0 +1,41 @@
1
+ import { annotationStateSchema } from '@fabrika/annotations/schemas'
2
+ import { z } from 'zod'
3
+
4
+ export const planResponseSchema = z.object({
5
+ id: z.string(),
6
+ type: z.enum(['select', 'radio', 'checkbox', 'text', 'range']),
7
+ label: z.string(),
8
+ value: z.unknown(),
9
+ options: z.array(z.string()).optional(),
10
+ required: z.boolean().optional(),
11
+ note: z.string().optional(),
12
+ }).passthrough().transform(response => ({ ...response, value: response.value }))
13
+
14
+ export const feedbackEntrySchema = z.object({
15
+ id: z.string(),
16
+ markdown: z.string(),
17
+ label: z.string().optional(),
18
+ required: z.boolean().optional(),
19
+ }).passthrough()
20
+
21
+ export const persistedStateSchema = annotationStateSchema.extend({
22
+ responses: z.array(
23
+ z.tuple([z.string(), planResponseSchema]).optional().catch(undefined),
24
+ )
25
+ .transform(entries => entries.filter(entry => entry !== undefined)).catch([]),
26
+ feedbackEntries: z.array(
27
+ z.tuple([z.string(), feedbackEntrySchema]).optional().catch(undefined),
28
+ )
29
+ .transform(entries => entries.filter(entry => entry !== undefined)).optional().catch([]),
30
+ })
31
+
32
+ export function parsePersistedState(value: unknown) {
33
+ const result = persistedStateSchema.safeParse(value)
34
+ return result.success ? result.data : null
35
+ }
36
+
37
+ export const feedbackResponseSchema = z.object({ feedback: z.string().nullish() })
38
+ export const filePreviewResponseSchema = z.union([
39
+ z.object({ error: z.string().min(1) }),
40
+ z.object({ content: z.string(), language: z.string().nullish() }),
41
+ ])
@@ -1,4 +1,4 @@
1
- export * from "preact/compat";
2
- export { default } from "preact/compat";
3
- export { jsx, jsxs } from "preact/jsx-runtime";
4
- export { createRoot, hydrateRoot } from "preact/compat/client";
1
+ export * from 'preact/compat'
2
+ export { default } from 'preact/compat'
3
+ export { createRoot, hydrateRoot } from 'preact/compat/client'
4
+ export { jsx, jsxs } from 'preact/jsx-runtime'
@@ -2,105 +2,52 @@
2
2
  // The React.createContext call MUST live here (not in AnnotationProvider)
3
3
  // so that both bundles reference the same context object.
4
4
 
5
- import { createContext, useContext, useEffect } from "react";
5
+ import { createContext, useContext, useEffect } from 'react'
6
6
 
7
- export interface AnnotationContext {
8
- before: string;
9
- after: string;
10
- hierarchy: string[];
11
- lineStart?: number;
12
- lineEnd?: number;
13
- }
14
-
15
- /** Identifies the person who created an annotation. For local annotations
16
- * this is the daemon owner (unset). For remote annotations it carries the
17
- * reviewer's self-reported name + stable anonymous id assigned by the worker. */
18
- export interface AnnotationAuthor {
19
- id: string;
20
- name: string;
21
- }
22
-
23
- /** An image or other file attached to an annotation. The richer sibling of
24
- * `images: string[]`, carrying a mime type alongside a URL that can point at
25
- * either a local upload (daemon) or a remote blob (CF Worker R2). */
26
- export interface AnnotationAttachment {
27
- url: string;
28
- mime?: string;
29
- }
30
-
31
- export interface Annotation {
32
- id: string;
33
- snippet: string;
34
- note: string;
35
- createdAt: string;
36
- filePath?: string;
37
- canvasFile?: string;
38
- context?: AnnotationContext;
39
- /** Attachments written by the local editor, as bare upload paths/URLs. Still
40
- * the only field it writes, and what persisted drafts hold. */
41
- images?: string[];
42
- /** Attachments that arrived over the wire, from a reviewer on a shared
43
- * canvas. Both fields render — see `renderAnnotation`. */
44
- attachments?: AnnotationAttachment[];
45
- /** "remote" annotations come from a shared view and are read-only locally. */
46
- source?: "local" | "remote";
47
- /** Only set for source === "remote". */
48
- author?: AnnotationAuthor;
49
- }
7
+ import type { AnnotationContextValue as BaseAnnotationContextValue } from '@fabrika/annotations/runtime'
8
+ export type { Annotation, AnnotationAttachment, AnnotationAuthor, AnnotationContext } from '@fabrika/annotations/runtime'
50
9
 
51
10
  export interface PlanResponse {
52
- id: string;
53
- type: "select" | "radio" | "checkbox" | "text" | "range";
54
- label: string;
55
- value: any;
56
- options?: string[];
57
- required?: boolean;
58
- note?: string;
11
+ id: string
12
+ type: 'select' | 'radio' | 'checkbox' | 'text' | 'range'
13
+ label: string
14
+ value: unknown
15
+ options?: string[]
16
+ required?: boolean
17
+ note?: string
59
18
  }
60
19
 
61
20
  export interface FeedbackEntry {
62
- id: string;
63
- markdown: string;
64
- label?: string;
65
- required?: boolean;
21
+ id: string
22
+ markdown: string
23
+ label?: string
24
+ required?: boolean
66
25
  }
67
26
 
68
- export interface AnnotationContextValue {
69
- annotations: Annotation[];
70
- addAnnotation: (snippet: string, note: string, filePath?: string) => void;
71
- addAnnotationWithId: (id: string, snippet: string, note: string, filePath?: string, context?: AnnotationContext, images?: string[], canvasFile?: string) => void;
72
- updateAnnotation: (id: string, note: string) => void;
73
- removeAnnotation: (id: string) => void;
74
- addAnnotationImage: (id: string, imagePath: string) => void;
75
- removeAnnotationImage: (id: string, imagePath: string) => void;
76
- generalNote: string;
77
- setGeneralNote: (text: string) => void;
78
- clearAll: () => void;
79
- activeAnnotationId: string | null;
80
- setActiveAnnotationId: (id: string | null) => void;
81
- /** Every answer held for this draft, including ones carried in from an
82
- * earlier revision. Controls read and write this one. */
83
- responses: Map<string, PlanResponse>;
84
- setResponse: (id: string, response: PlanResponse) => void;
85
- /** `responses` narrowed to the questions this revision actually asks.
86
- * Everything that reports feedback — markdown, validation, "has content" —
87
- * reads this instead, so a carried-over answer to a since-removed question
88
- * is not submitted. Identical to `responses` until the host supplies the
89
- * canvas file list and every one of them has rendered. */
90
- submittableResponses: Map<string, PlanResponse>;
91
- registerResponse: (id: string) => void;
92
- registerCanvasRendered: (filename: string) => void;
93
- feedbackEntries: Map<string, FeedbackEntry>;
94
- setFeedbackEntry: (id: string, entry: FeedbackEntry) => void;
95
- removeFeedbackEntry: (id: string) => void;
96
- /** Canvas is rendered read-only (reviewing history). */
97
- isReadOnly: boolean;
27
+ export interface AnnotationContextValue extends BaseAnnotationContextValue {
28
+ /** Every answer held for this draft, including ones carried in from an
29
+ * earlier revision. Controls read and write this one. */
30
+ responses: Map<string, PlanResponse>
31
+ setResponse: (id: string, response: PlanResponse) => void
32
+ /** `responses` narrowed to the questions this revision actually asks.
33
+ * Everything that reports feedback — markdown, validation, "has content" —
34
+ * reads this instead, so a carried-over answer to a since-removed question
35
+ * is not submitted. Identical to `responses` until the host supplies the
36
+ * canvas file list and every one of them has rendered. */
37
+ submittableResponses: Map<string, PlanResponse>
38
+ registerResponse: (id: string) => void
39
+ registerCanvasRendered: (filename: string) => void
40
+ feedbackEntries: Map<string, FeedbackEntry>
41
+ setFeedbackEntry: (id: string, entry: FeedbackEntry) => void
42
+ removeFeedbackEntry: (id: string) => void
43
+ /** Canvas is rendered read-only (reviewing history). */
44
+ isReadOnly: boolean
98
45
  }
99
46
 
100
- export const AnnotationCtx = createContext<AnnotationContextValue>(null!);
47
+ export const AnnotationCtx = createContext<AnnotationContextValue>(null!)
101
48
 
102
49
  export function useAnnotations(): AnnotationContextValue {
103
- return useContext(AnnotationCtx);
50
+ return useContext(AnnotationCtx)
104
51
  }
105
52
 
106
53
  /** Record that this revision asks the question, so submission can tell it from
@@ -108,42 +55,42 @@ export function useAnnotations(): AnnotationContextValue {
108
55
  * cleanup: unmounting means the reader navigated away, not that the question
109
56
  * disappeared. */
110
57
  export function useResponseRegistration(id: string): void {
111
- const { registerResponse } = useAnnotations();
112
- useEffect(() => {
113
- registerResponse(id);
114
- }, [id, registerResponse]);
58
+ const { registerResponse } = useAnnotations()
59
+ useEffect(() => {
60
+ registerResponse(id)
61
+ }, [id, registerResponse])
115
62
  }
116
63
 
117
64
  export function useFeedback(
118
- id: string,
119
- markdown: string,
120
- options?: { label?: string; required?: boolean },
65
+ id: string,
66
+ markdown: string,
67
+ options?: { label?: string; required?: boolean },
121
68
  ): void {
122
- const { setFeedbackEntry, removeFeedbackEntry } = useAnnotations();
123
- useEffect(() => {
124
- setFeedbackEntry(id, {
125
- id,
126
- markdown,
127
- label: options?.label,
128
- required: options?.required,
129
- });
130
- return () => removeFeedbackEntry(id);
131
- }, [id, markdown, options?.label, options?.required]);
69
+ const { setFeedbackEntry, removeFeedbackEntry } = useAnnotations()
70
+ useEffect(() => {
71
+ setFeedbackEntry(id, {
72
+ id,
73
+ markdown,
74
+ label: options?.label,
75
+ required: options?.required,
76
+ })
77
+ return () => removeFeedbackEntry(id)
78
+ }, [id, markdown, options?.label, options?.required])
132
79
  }
133
80
 
134
- export { SessionContext } from "./SessionContext";
81
+ export { SessionContext } from '@fabrika/annotations/runtime'
135
82
 
136
83
  // CanvasFile context — tells components which canvas file they belong to
137
- export const CanvasFileCtx = createContext<string>("");
84
+ export const CanvasFileCtx = createContext<string>('')
138
85
  export function useCanvasFile(): string {
139
- return useContext(CanvasFileCtx);
86
+ return useContext(CanvasFileCtx)
140
87
  }
141
88
 
142
89
  // ActiveView navigation — allows components (e.g. FilePreview) to open files
143
- export type ActiveView = { type: "overview" } | { type: "canvas"; filename: string } | { type: "file"; path: string };
90
+ export type ActiveView = { type: 'overview' } | { type: 'canvas'; filename: string } | { type: 'file'; path: string }
144
91
 
145
92
  export const ActiveViewCtx = createContext<{
146
- setActiveView: (v: ActiveView) => void;
93
+ setActiveView: (v: ActiveView) => void
147
94
  }>({
148
- setActiveView: () => {},
149
- });
95
+ setActiveView: () => {},
96
+ })
@@ -2,6 +2,7 @@
2
2
 
3
3
  @import "tailwindcss";
4
4
  @import "./theme.css";
5
+ @import "@fabrika/annotations/styles.css";
5
6
  @source "./**/*.{tsx,ts,jsx,js}";
6
7
 
7
8
  /* ── Non-theme CSS variables (not used by Tailwind utilities, just custom CSS) ── */
@@ -96,80 +97,6 @@ code, pre, .font-mono {
96
97
  font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
97
98
  }
98
99
 
99
- /* Annotation inline marks */
100
- mark[data-annotation-id] {
101
- background: var(--color-highlight-annotation);
102
- border-bottom: 1.5px solid var(--color-highlight-bg);
103
- border-radius: 2px;
104
- cursor: pointer;
105
- transition: background 150ms ease, box-shadow 150ms ease;
106
- color: inherit;
107
- padding: 1px 0;
108
- }
109
-
110
- mark[data-annotation-id].mark-active {
111
- background: var(--color-highlight-active);
112
- border-bottom: 2px solid var(--color-highlight-border);
113
- box-shadow: 0 0 0 1px var(--color-highlight-border);
114
- }
115
-
116
- @keyframes ann-flash {
117
- 0%, 100% { background: var(--color-highlight-annotation); }
118
- 25%, 75% { background: var(--color-highlight-active); }
119
- }
120
-
121
- .ann-flash {
122
- animation: ann-flash 0.6s ease 2;
123
- }
124
-
125
- /* Region annotations — a normalized box drawn over an image block. The host
126
- positions the boxes, so every host that marks one gets them placed right. */
127
- [data-annotation-image] {
128
- position: relative;
129
- cursor: crosshair;
130
- touch-action: none;
131
- }
132
-
133
- .ann-region {
134
- position: absolute;
135
- background: var(--color-highlight-annotation);
136
- border: 2px solid var(--color-highlight-bg);
137
- border-radius: 2px;
138
- cursor: pointer;
139
- transition: background 150ms ease, box-shadow 150ms ease;
140
- }
141
-
142
- .ann-region[data-annotation-region="ellipse"] {
143
- border-radius: 50%;
144
- }
145
-
146
- .ann-region.mark-active {
147
- background: var(--color-highlight-active);
148
- border-color: var(--color-highlight-border);
149
- box-shadow: 0 0 0 1px var(--color-highlight-border);
150
- }
151
-
152
- /* The box being dragged out: not yet an annotation, and never a hit target. */
153
- .ann-region-draft {
154
- pointer-events: none;
155
- border-style: dashed;
156
- border-color: var(--color-highlight-border);
157
- }
158
-
159
- /* Annotation highlight (legacy) */
160
- .annotation-highlight {
161
- background: var(--color-highlight-bg);
162
- border-bottom: 2px solid var(--color-highlight-border);
163
- border-radius: 2px;
164
- cursor: pointer;
165
- transition: background 150ms ease;
166
- }
167
-
168
- .annotation-highlight:hover,
169
- .annotation-highlight.active {
170
- background: var(--color-highlight-active);
171
- }
172
-
173
100
  /* Selection color */
174
101
  ::selection {
175
102
  background-color: var(--color-highlight-active);