@ascribed/review 0.2.0 → 0.2.1-next.11

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.
@@ -1 +1 @@
1
- export declare const OVERLAY_CSS = "\n:host {\n all: initial;\n --text: var(--ascribe-review-text, CanvasText);\n --muted: var(--ascribe-review-muted, #5b6475);\n --line: var(--ascribe-review-border, #d3d8e0);\n --surface: var(--ascribe-review-surface, Canvas);\n --thread: var(--ascribe-review-thread, #f5f7fb);\n --accent: var(--ascribe-review-focus, #1f6feb);\n --accent-text: var(--ascribe-review-accent-text, #ffffff);\n --input: var(--ascribe-review-input, var(--surface));\n --flash: var(--ascribe-review-flash, #fff6bf);\n --added: var(--ascribe-review-added, #1a7f37);\n --changed: var(--ascribe-review-changed, #8a5a00);\n --changed-background: var(--ascribe-review-changed-background, #fff1c2);\n --removed: var(--ascribe-review-removed, #c4222d);\n --removed-background: var(--ascribe-review-removed-background, #ffe4e2);\n --mono: var(--ascribe-review-mono, ui-monospace, monospace);\n font-family: var(--ascribe-review-font, system-ui, -apple-system, \"Segoe UI\", sans-serif);\n font-size: 12.5px;\n line-height: 1.45;\n color: var(--text);\n}\n* {\n box-sizing: border-box;\n}\n[hidden] {\n display: none !important;\n}\nbutton,\ntextarea,\ninput {\n font: inherit;\n color: inherit;\n}\nbutton {\n cursor: pointer;\n}\nbutton:disabled,\nbutton[aria-disabled=\"true\"] {\n cursor: not-allowed;\n opacity: 0.5;\n}\nbutton:focus-visible,\ntextarea:focus-visible,\ninput:focus-visible,\n.thread:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 1px;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n/* Buttons */\n.ghost {\n border: 1px solid var(--line);\n background: var(--surface);\n border-radius: 5px;\n padding: 2px 8px;\n font-size: 12px;\n min-height: 24px;\n}\n.ghost:hover:not(:disabled):not([aria-disabled=\"true\"]) {\n border-color: var(--accent);\n color: var(--accent);\n}\n.primary {\n border: 1px solid var(--accent);\n background: var(--accent);\n color: var(--accent-text);\n border-radius: 5px;\n padding: 2px 10px;\n font-size: 12.5px;\n font-weight: 600;\n min-height: 24px;\n}\n.danger {\n border: 1px solid var(--removed);\n background: var(--removed);\n color: var(--surface);\n border-radius: 5px;\n padding: 2px 10px;\n font-size: 12.5px;\n font-weight: 600;\n min-height: 24px;\n}\n.link {\n border: 0;\n background: transparent;\n color: var(--accent);\n padding: 0;\n font-size: inherit;\n text-align: inherit;\n}\na.link {\n text-decoration: none;\n}\n.link:hover {\n text-decoration: underline;\n}\n.hint {\n color: var(--muted);\n font-size: 11.5px;\n}\n.refuse,\n.error {\n color: var(--removed);\n font-size: 12px;\n}\n.spacer {\n flex: 1;\n}\n\n/* The layer over the page: the column, markers' panel, dialogs. */\n.layer {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n.layer > *,\n.column > * {\n pointer-events: auto;\n}\n.column {\n position: absolute;\n top: 0;\n width: 250px;\n pointer-events: none;\n}\n.slot {\n position: absolute;\n left: 0;\n right: 0;\n}\n.slot > .thread,\n.slot > .composer {\n margin: 0 0 6px;\n}\n.slot.linked .thread,\n.slot.linked .composer {\n border-color: var(--accent);\n}\n.highlight {\n position: absolute;\n pointer-events: none;\n background: color-mix(in srgb, var(--flash) 55%, transparent);\n border-radius: 3px;\n /* Over the page's text, a tint that keeps the text as it was: multiplied\n on a light page, screened on a dark one. */\n mix-blend-mode: var(--ascribe-review-blend, multiply);\n}\nsvg.connector {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n overflow: visible;\n pointer-events: none;\n}\nsvg.connector path {\n fill: none;\n stroke: var(--accent);\n stroke-width: 1.5;\n}\n\n/* The block tools: the Comment button, and the count marker at narrow widths. */\n.tool {\n border: 1px solid var(--line);\n background: var(--surface);\n border-radius: 5px;\n font-size: 11.5px;\n padding: 0 7px;\n min-height: 22px;\n color: var(--accent);\n white-space: nowrap;\n}\n.hint-count {\n margin-inline-start: 2px;\n font-size: 11px;\n font-weight: 600;\n color: var(--accent);\n}\n\n.marker {\n border: 1px solid var(--accent);\n background: var(--surface);\n color: var(--accent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n padding: 0 8px;\n min-width: 30px;\n min-height: 24px;\n}\n\n/* Threads */\n.thread {\n background: var(--thread);\n border: 1px solid var(--line);\n border-radius: 6px;\n margin: 6px 0 10px;\n}\n.th {\n display: flex;\n flex-wrap: wrap;\n gap: 3px 8px;\n align-items: center;\n padding: 5px 9px 2px;\n font-size: 11.5px;\n color: var(--muted);\n}\n.acts {\n display: flex;\n flex-wrap: wrap;\n gap: 2px 12px;\n padding: 0 9px 5px;\n border-bottom: 1px solid var(--line);\n font-size: 11.5px;\n}\n.thread.collapsed .acts {\n border-bottom: 0;\n}\n.orig,\n.inside {\n padding: 5px 9px;\n border-bottom: 1px solid var(--line);\n font-size: 11.5px;\n color: var(--muted);\n}\n.orig q {\n color: var(--text);\n white-space: pre-wrap;\n}\n.badge {\n border-radius: 999px;\n padding: 0 7px;\n font-size: 10.5px;\n font-weight: 600;\n border: 1px solid currentColor;\n white-space: nowrap;\n}\n.badge.unsent {\n color: var(--changed);\n}\n.badge.resolved {\n color: var(--added);\n}\n.badge.outdated {\n color: var(--muted);\n}\n.badge.summary {\n color: var(--accent);\n}\n.badge.removed,\n.badge.detached {\n color: var(--removed);\n}\n.comment {\n display: grid;\n grid-template-columns: 22px minmax(0, 1fr);\n gap: 7px;\n padding: 6px 9px;\n}\n.comment + .comment {\n border-top: 1px dashed var(--line);\n}\n.avatar {\n width: 22px;\n height: 22px;\n border-radius: 50%;\n background: var(--accent);\n color: var(--accent-text);\n display: grid;\n place-items: center;\n font-size: 9.5px;\n font-weight: 700;\n}\n.who {\n font-weight: 600;\n}\n.when {\n color: var(--muted);\n font-size: 11px;\n margin-left: 6px;\n}\n.body {\n overflow-wrap: anywhere;\n}\n.body > :first-child {\n margin-top: 1px;\n}\n.body > :last-child {\n margin-bottom: 0;\n}\n.body p,\n.body ul,\n.body ol,\n.body blockquote,\n.body pre {\n margin: 4px 0;\n}\n.body ul,\n.body ol {\n padding-left: 18px;\n}\n.body blockquote {\n border-left: 3px solid var(--line);\n padding-left: 8px;\n color: var(--muted);\n}\n.body code {\n font-family: var(--mono);\n font-size: 0.92em;\n background: color-mix(in srgb, var(--muted) 14%, transparent);\n padding: 0.05em 0.3em;\n border-radius: 3px;\n}\n.body pre {\n overflow-x: auto;\n padding: 6px 8px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--muted) 14%, transparent);\n}\n.body pre code {\n background: none;\n padding: 0;\n}\n.body a {\n color: var(--accent);\n}\n.reply {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 6px 9px 8px;\n border-top: 1px solid var(--line);\n}\n.reply textarea {\n flex: 1 1 100%;\n min-width: 80px;\n padding: 2px 8px;\n border: 1px solid var(--line);\n border-radius: 5px;\n background: var(--input);\n resize: vertical;\n field-sizing: content;\n min-height: 26px;\n max-height: 160px;\n}\n.reply .error,\n.reply .hint {\n flex-basis: 100%;\n}\n.composer {\n background: var(--thread);\n border: 1px solid var(--accent);\n border-radius: 6px;\n padding: 8px 10px;\n margin: 6px 0 10px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.composer textarea {\n width: 100%;\n min-height: 56px;\n resize: vertical;\n padding: 6px 8px;\n border: 1px solid var(--line);\n border-radius: 5px;\n background: var(--input);\n}\n.row {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n align-items: center;\n}\n\n/* The detached list, above the page */\n.detached {\n margin: 12px 0;\n border: 1px dashed var(--line);\n border-radius: 6px;\n padding: 8px 10px 2px;\n color: var(--muted);\n max-width: 520px;\n}\n.detached .thread {\n color: var(--text);\n}\n\n/* The unsent bar, at the bottom */\n.unsent-bar {\n background: var(--changed-background);\n border-top: 1px solid var(--line);\n padding: 6px 10px;\n display: flex;\n flex-wrap: wrap;\n gap: 6px 10px;\n align-items: center;\n}\n.unsent-bar .hint {\n font-size: 12px;\n}\n\n/* The panel for one block's threads, at narrow widths */\n.sheet {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n max-height: 62vh;\n overflow: auto;\n background: var(--surface);\n border-top: 2px solid var(--accent);\n box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.22);\n padding: 8px 12px 10px;\n z-index: 4;\n}\n.sheet .head {\n display: flex;\n gap: 8px;\n align-items: center;\n font-size: 12px;\n color: var(--muted);\n}\n\n/* Dialogs */\n.backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.4);\n z-index: 8;\n display: grid;\n place-items: center;\n padding: 12px;\n}\n.dialog {\n background: var(--surface);\n border: 1px solid var(--line);\n border-radius: 8px;\n width: min(480px, 100%);\n max-height: 100%;\n overflow: auto;\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n font-size: 13px;\n}\n.dialog h2 {\n margin: 0;\n font-size: 14px;\n}\n.dialog ul.unsent-list {\n margin: 0;\n padding-left: 18px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.dialog fieldset {\n border: 0;\n padding: 0;\n margin: 0;\n display: flex;\n flex-wrap: wrap;\n gap: 4px 14px;\n}\n.dialog textarea {\n width: 100%;\n min-height: 56px;\n resize: vertical;\n padding: 6px 8px;\n border: 1px solid var(--line);\n border-radius: 5px;\n background: var(--input);\n}\n.confirm {\n border: 1px solid var(--removed);\n background: var(--removed-background);\n border-radius: 6px;\n padding: 8px 10px;\n}\n.segmented {\n display: inline-flex;\n flex-wrap: wrap;\n align-self: flex-start;\n border: 1px solid var(--line);\n border-radius: 5px;\n overflow: hidden;\n}\n.segmented button {\n border: 0;\n background: transparent;\n padding: 2px 9px;\n font-size: 12px;\n color: var(--muted);\n min-height: 24px;\n}\n.segmented button + button {\n border-left: 1px solid var(--line);\n}\n.segmented button[aria-pressed=\"true\"] {\n background: var(--accent);\n color: var(--accent-text);\n}\n.thread-list {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n.thread-list li + li {\n border-top: 1px solid var(--line);\n}\n.thread-list button {\n display: flex;\n flex-direction: column;\n gap: 1px;\n width: 100%;\n text-align: left;\n border: 0;\n background: transparent;\n padding: 6px 4px;\n}\n.thread-list button:hover,\n.thread-list button:focus-visible {\n background: color-mix(in srgb, var(--muted) 12%, transparent);\n}\n.thread-list .where {\n font-size: 11.5px;\n color: var(--muted);\n}\n.thread-list .first {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.flash {\n animation: flash 1.4s ease-out;\n}\n@keyframes flash {\n from {\n box-shadow: 0 0 0 3px var(--accent);\n }\n to {\n box-shadow: 0 0 0 0 transparent;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .flash {\n animation: none;\n outline: 2px solid var(--accent);\n }\n}\n";
1
+ export declare const OVERLAY_CSS = "\n:host {\n all: initial;\n /* Generated from design/tokens.toml by scripts/design/tokens.ts: host, light. */\n --muted: var(--ascribe-review-muted, #676c75);\n --line: var(--ascribe-review-border, #e3e6ec);\n --thread: var(--ascribe-review-thread, #f7f9fd);\n --accent: var(--ascribe-review-focus, #5962e8);\n --accent-text: var(--ascribe-review-accent-text, #ffffff);\n --flash: var(--ascribe-review-flash, #fee9d0);\n --added: var(--ascribe-review-added, #057256);\n --changed: var(--ascribe-review-changed, #845502);\n --changed-background: var(--ascribe-review-changed-background, #fee9d0);\n --removed: var(--ascribe-review-removed, #ca3f02);\n --removed-background: var(--ascribe-review-removed-background, #fef4f1);\n --mono: var(--ascribe-review-mono, ui-monospace, monospace);\n font-family: var(--ascribe-review-font, system-ui, -apple-system, \"Segoe UI\", sans-serif);\n /* End of generated host. */\n --text: var(--ascribe-review-text, CanvasText);\n --surface: var(--ascribe-review-surface, Canvas);\n --input: var(--ascribe-review-input, var(--surface));\n font-size: 12.5px;\n line-height: 1.45;\n color: var(--text);\n}\n* {\n box-sizing: border-box;\n}\n[hidden] {\n display: none !important;\n}\nbutton,\ntextarea,\ninput {\n font: inherit;\n color: inherit;\n}\nbutton {\n cursor: pointer;\n}\nbutton:disabled,\nbutton[aria-disabled=\"true\"] {\n cursor: not-allowed;\n opacity: 0.5;\n}\nbutton:focus-visible,\ntextarea:focus-visible,\ninput:focus-visible,\n.thread:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 1px;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n/* Buttons */\n.ghost {\n border: 1px solid var(--line);\n background: var(--surface);\n border-radius: 5px;\n padding: 2px 8px;\n font-size: 12px;\n min-height: 24px;\n}\n.ghost:hover:not(:disabled):not([aria-disabled=\"true\"]) {\n border-color: var(--accent);\n color: var(--accent);\n}\n.primary {\n border: 1px solid var(--accent);\n background: var(--accent);\n color: var(--accent-text);\n border-radius: 5px;\n padding: 2px 10px;\n font-size: 12.5px;\n font-weight: 600;\n min-height: 24px;\n}\n.danger {\n border: 1px solid var(--removed);\n background: var(--removed);\n color: var(--surface);\n border-radius: 5px;\n padding: 2px 10px;\n font-size: 12.5px;\n font-weight: 600;\n min-height: 24px;\n}\n.link {\n border: 0;\n background: transparent;\n color: var(--accent);\n padding: 0;\n font-size: inherit;\n text-align: inherit;\n}\na.link {\n text-decoration: none;\n}\n.link:hover {\n text-decoration: underline;\n}\n.hint {\n color: var(--muted);\n font-size: 11.5px;\n}\n.refuse,\n.error {\n color: var(--removed);\n font-size: 12px;\n}\n.spacer {\n flex: 1;\n}\n\n/* The layer over the page: the column, markers' panel, dialogs. */\n.layer {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n.layer > *,\n.column > * {\n pointer-events: auto;\n}\n.column {\n position: absolute;\n top: 0;\n width: 250px;\n pointer-events: none;\n}\n.slot {\n position: absolute;\n left: 0;\n right: 0;\n}\n.slot > .thread,\n.slot > .composer {\n margin: 0 0 6px;\n}\n.slot.linked .thread,\n.slot.linked .composer {\n border-color: var(--accent);\n}\n.highlight {\n position: absolute;\n pointer-events: none;\n background: color-mix(in srgb, var(--flash) 55%, transparent);\n border-radius: 3px;\n /* Over the page's text, a tint that keeps the text as it was: multiplied\n on a light page, screened on a dark one. */\n mix-blend-mode: var(--ascribe-review-blend, multiply);\n}\nsvg.connector {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n overflow: visible;\n pointer-events: none;\n}\nsvg.connector path {\n fill: none;\n stroke: var(--accent);\n stroke-width: 1.5;\n}\n\n/* The block tools: the Comment button, and the count marker at narrow widths. */\n.tool {\n border: 1px solid var(--line);\n background: var(--surface);\n border-radius: 5px;\n font-size: 11.5px;\n padding: 0 7px;\n min-height: 22px;\n color: var(--accent);\n white-space: nowrap;\n}\n.hint-count {\n margin-inline-start: 2px;\n font-size: 11px;\n font-weight: 600;\n color: var(--accent);\n}\n\n.marker {\n border: 1px solid var(--accent);\n background: var(--surface);\n color: var(--accent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n padding: 0 8px;\n min-width: 30px;\n min-height: 24px;\n}\n\n/* Threads */\n.thread {\n background: var(--thread);\n border: 1px solid var(--line);\n border-radius: 6px;\n margin: 6px 0 10px;\n}\n.th {\n display: flex;\n flex-wrap: wrap;\n gap: 3px 8px;\n align-items: center;\n padding: 5px 9px 2px;\n font-size: 11.5px;\n color: var(--muted);\n}\n.acts {\n display: flex;\n flex-wrap: wrap;\n gap: 2px 12px;\n padding: 0 9px 5px;\n border-bottom: 1px solid var(--line);\n font-size: 11.5px;\n}\n.thread.collapsed .acts {\n border-bottom: 0;\n}\n.orig,\n.inside {\n padding: 5px 9px;\n border-bottom: 1px solid var(--line);\n font-size: 11.5px;\n color: var(--muted);\n}\n.orig q {\n color: var(--text);\n white-space: pre-wrap;\n}\n.badge {\n border-radius: 999px;\n padding: 0 7px;\n font-size: 10.5px;\n font-weight: 600;\n border: 1px solid currentColor;\n white-space: nowrap;\n}\n.badge.unsent {\n color: var(--changed);\n}\n.badge.resolved {\n color: var(--added);\n}\n.badge.outdated {\n color: var(--muted);\n}\n.badge.summary {\n color: var(--accent);\n}\n.badge.removed,\n.badge.detached {\n color: var(--removed);\n}\n.comment {\n display: grid;\n grid-template-columns: 22px minmax(0, 1fr);\n gap: 7px;\n padding: 6px 9px;\n}\n.comment + .comment {\n border-top: 1px dashed var(--line);\n}\n.avatar {\n width: 22px;\n height: 22px;\n border-radius: 50%;\n background: var(--accent);\n color: var(--accent-text);\n display: grid;\n place-items: center;\n font-size: 9.5px;\n font-weight: 700;\n}\n.who {\n font-weight: 600;\n}\n.when {\n color: var(--muted);\n font-size: 11px;\n margin-left: 6px;\n}\n.body {\n overflow-wrap: anywhere;\n}\n.body > :first-child {\n margin-top: 1px;\n}\n.body > :last-child {\n margin-bottom: 0;\n}\n.body p,\n.body ul,\n.body ol,\n.body blockquote,\n.body pre {\n margin: 4px 0;\n}\n.body ul,\n.body ol {\n padding-left: 18px;\n}\n.body blockquote {\n border-left: 3px solid var(--line);\n padding-left: 8px;\n color: var(--muted);\n}\n.body code,\n.thread-list .where code {\n font-family: var(--mono);\n font-size: 0.92em;\n background: color-mix(in srgb, var(--muted) 14%, transparent);\n padding: 0.05em 0.3em;\n border-radius: 3px;\n}\n.body pre {\n overflow-x: auto;\n padding: 6px 8px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--muted) 14%, transparent);\n}\n.body pre code {\n background: none;\n padding: 0;\n}\n.body a {\n color: var(--accent);\n}\n.reply {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 6px 9px 8px;\n border-top: 1px solid var(--line);\n}\n.reply textarea {\n flex: 1 1 100%;\n min-width: 80px;\n padding: 2px 8px;\n border: 1px solid var(--line);\n border-radius: 5px;\n background: var(--input);\n resize: vertical;\n field-sizing: content;\n min-height: 26px;\n max-height: 160px;\n}\n.reply .error,\n.reply .hint {\n flex-basis: 100%;\n}\n.composer {\n background: var(--thread);\n border: 1px solid var(--accent);\n border-radius: 6px;\n padding: 8px 10px;\n margin: 6px 0 10px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.composer textarea {\n width: 100%;\n min-height: 56px;\n resize: vertical;\n padding: 6px 8px;\n border: 1px solid var(--line);\n border-radius: 5px;\n background: var(--input);\n}\n.row {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n align-items: center;\n}\n\n/* The detached list, above the page */\n.detached {\n margin: 12px 0;\n border: 1px dashed var(--line);\n border-radius: 6px;\n padding: 8px 10px 2px;\n color: var(--muted);\n max-width: 520px;\n}\n.detached .thread {\n color: var(--text);\n}\n\n/* The unsent bar, at the bottom */\n.unsent-bar {\n background: var(--changed-background);\n border-top: 1px solid var(--line);\n padding: 6px 10px;\n display: flex;\n flex-wrap: wrap;\n gap: 6px 10px;\n align-items: center;\n}\n.unsent-bar .hint {\n color: var(--text);\n font-size: 12px;\n}\n\n/* The panel for one block's threads, at narrow widths */\n.sheet {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n max-height: 62vh;\n overflow: auto;\n background: var(--surface);\n border-top: 2px solid var(--accent);\n /* Generated from design/tokens.toml by scripts/design/tokens.ts: sheet, light. */\n box-shadow: 0 -8px 24px rgb(0 0 0 / 0.22);\n /* End of generated sheet. */\n padding: 8px 12px 10px;\n z-index: 4;\n}\n.sheet .head {\n display: flex;\n gap: 8px;\n align-items: center;\n font-size: 12px;\n color: var(--muted);\n}\n\n/* Dialogs */\n.backdrop {\n position: fixed;\n inset: 0;\n /* Generated from design/tokens.toml by scripts/design/tokens.ts: backdrop, light. */\n background: rgb(0 0 0 / 0.4);\n /* End of generated backdrop. */\n z-index: 8;\n display: grid;\n place-items: center;\n padding: 12px;\n}\n.dialog {\n background: var(--surface);\n border: 1px solid var(--line);\n border-radius: 8px;\n width: min(480px, 100%);\n max-height: 100%;\n overflow: auto;\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n font-size: 13px;\n}\n.dialog h2 {\n margin: 0;\n font-size: 14px;\n}\n.dialog ul.unsent-list {\n margin: 0;\n padding-left: 18px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.dialog fieldset {\n border: 0;\n padding: 0;\n margin: 0;\n display: flex;\n flex-wrap: wrap;\n gap: 4px 14px;\n}\n.dialog textarea {\n width: 100%;\n min-height: 56px;\n resize: vertical;\n padding: 6px 8px;\n border: 1px solid var(--line);\n border-radius: 5px;\n background: var(--input);\n}\n.confirm {\n border: 1px solid var(--removed);\n background: var(--removed-background);\n border-radius: 6px;\n padding: 8px 10px;\n}\n.segmented {\n display: inline-flex;\n flex-wrap: wrap;\n align-self: flex-start;\n border: 1px solid var(--line);\n border-radius: 5px;\n overflow: hidden;\n}\n.segmented button {\n border: 0;\n background: transparent;\n padding: 2px 9px;\n font-size: 12px;\n color: var(--muted);\n min-height: 24px;\n}\n.segmented button + button {\n border-left: 1px solid var(--line);\n}\n.segmented button[aria-pressed=\"true\"] {\n background: var(--accent);\n color: var(--accent-text);\n}\n.thread-list {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n.thread-list li + li {\n border-top: 1px solid var(--line);\n}\n.thread-list button {\n display: flex;\n flex-direction: column;\n gap: 1px;\n width: 100%;\n text-align: left;\n border: 0;\n background: transparent;\n padding: 6px 4px;\n}\n.thread-list button:hover,\n.thread-list button:focus-visible {\n background: color-mix(in srgb, var(--muted) 12%, transparent);\n}\n.thread-list .where {\n font-size: 11.5px;\n color: var(--muted);\n}\n.thread-list .first {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.flash {\n animation: flash 1.4s ease-out;\n}\n@keyframes flash {\n from {\n box-shadow: 0 0 0 3px var(--accent);\n }\n to {\n box-shadow: 0 0 0 0 transparent;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .flash {\n animation: none;\n outline: 2px solid var(--accent);\n }\n}\n";
@@ -6,22 +6,24 @@
6
6
  export const OVERLAY_CSS = `
7
7
  :host {
8
8
  all: initial;
9
- --text: var(--ascribe-review-text, CanvasText);
10
- --muted: var(--ascribe-review-muted, #5b6475);
11
- --line: var(--ascribe-review-border, #d3d8e0);
12
- --surface: var(--ascribe-review-surface, Canvas);
13
- --thread: var(--ascribe-review-thread, #f5f7fb);
14
- --accent: var(--ascribe-review-focus, #1f6feb);
9
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: host, light. */
10
+ --muted: var(--ascribe-review-muted, #676c75);
11
+ --line: var(--ascribe-review-border, #e3e6ec);
12
+ --thread: var(--ascribe-review-thread, #f7f9fd);
13
+ --accent: var(--ascribe-review-focus, #5962e8);
15
14
  --accent-text: var(--ascribe-review-accent-text, #ffffff);
16
- --input: var(--ascribe-review-input, var(--surface));
17
- --flash: var(--ascribe-review-flash, #fff6bf);
18
- --added: var(--ascribe-review-added, #1a7f37);
19
- --changed: var(--ascribe-review-changed, #8a5a00);
20
- --changed-background: var(--ascribe-review-changed-background, #fff1c2);
21
- --removed: var(--ascribe-review-removed, #c4222d);
22
- --removed-background: var(--ascribe-review-removed-background, #ffe4e2);
15
+ --flash: var(--ascribe-review-flash, #fee9d0);
16
+ --added: var(--ascribe-review-added, #057256);
17
+ --changed: var(--ascribe-review-changed, #845502);
18
+ --changed-background: var(--ascribe-review-changed-background, #fee9d0);
19
+ --removed: var(--ascribe-review-removed, #ca3f02);
20
+ --removed-background: var(--ascribe-review-removed-background, #fef4f1);
23
21
  --mono: var(--ascribe-review-mono, ui-monospace, monospace);
24
22
  font-family: var(--ascribe-review-font, system-ui, -apple-system, "Segoe UI", sans-serif);
23
+ /* End of generated host. */
24
+ --text: var(--ascribe-review-text, CanvasText);
25
+ --surface: var(--ascribe-review-surface, Canvas);
26
+ --input: var(--ascribe-review-input, var(--surface));
25
27
  font-size: 12.5px;
26
28
  line-height: 1.45;
27
29
  color: var(--text);
@@ -319,7 +321,8 @@ svg.connector path {
319
321
  padding-left: 8px;
320
322
  color: var(--muted);
321
323
  }
322
- .body code {
324
+ .body code,
325
+ .thread-list .where code {
323
326
  font-family: var(--mono);
324
327
  font-size: 0.92em;
325
328
  background: color-mix(in srgb, var(--muted) 14%, transparent);
@@ -412,6 +415,7 @@ svg.connector path {
412
415
  align-items: center;
413
416
  }
414
417
  .unsent-bar .hint {
418
+ color: var(--text);
415
419
  font-size: 12px;
416
420
  }
417
421
 
@@ -425,7 +429,9 @@ svg.connector path {
425
429
  overflow: auto;
426
430
  background: var(--surface);
427
431
  border-top: 2px solid var(--accent);
428
- box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.22);
432
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: sheet, light. */
433
+ box-shadow: 0 -8px 24px rgb(0 0 0 / 0.22);
434
+ /* End of generated sheet. */
429
435
  padding: 8px 12px 10px;
430
436
  z-index: 4;
431
437
  }
@@ -441,7 +447,9 @@ svg.connector path {
441
447
  .backdrop {
442
448
  position: fixed;
443
449
  inset: 0;
444
- background: rgba(0, 0, 0, 0.4);
450
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: backdrop, light. */
451
+ background: rgb(0 0 0 / 0.4);
452
+ /* End of generated backdrop. */
445
453
  z-index: 8;
446
454
  display: grid;
447
455
  place-items: center;
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","sourceRoot":"","sources":["../../src/overlay/style.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,kDAAkD;AAElD,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmjB1B,CAAC","sourcesContent":["// The overlay's stylesheet. It applies inside the overlay's shadow roots only,\n// so the page's styles don't reach the overlay and the overlay's don't reach\n// the page. Colors come from the page's --ascribe-review-* custom properties\n// (`marks.css` sets them, with light and dark values; a host can set its own),\n// which reach inside shadow roots by inheritance.\n\nexport const OVERLAY_CSS = `\n:host {\n all: initial;\n --text: var(--ascribe-review-text, CanvasText);\n --muted: var(--ascribe-review-muted, #5b6475);\n --line: var(--ascribe-review-border, #d3d8e0);\n --surface: var(--ascribe-review-surface, Canvas);\n --thread: var(--ascribe-review-thread, #f5f7fb);\n --accent: var(--ascribe-review-focus, #1f6feb);\n --accent-text: var(--ascribe-review-accent-text, #ffffff);\n --input: var(--ascribe-review-input, var(--surface));\n --flash: var(--ascribe-review-flash, #fff6bf);\n --added: var(--ascribe-review-added, #1a7f37);\n --changed: var(--ascribe-review-changed, #8a5a00);\n --changed-background: var(--ascribe-review-changed-background, #fff1c2);\n --removed: var(--ascribe-review-removed, #c4222d);\n --removed-background: var(--ascribe-review-removed-background, #ffe4e2);\n --mono: var(--ascribe-review-mono, ui-monospace, monospace);\n font-family: var(--ascribe-review-font, system-ui, -apple-system, \"Segoe UI\", sans-serif);\n font-size: 12.5px;\n line-height: 1.45;\n color: var(--text);\n}\n* {\n box-sizing: border-box;\n}\n[hidden] {\n display: none !important;\n}\nbutton,\ntextarea,\ninput {\n font: inherit;\n color: inherit;\n}\nbutton {\n cursor: pointer;\n}\nbutton:disabled,\nbutton[aria-disabled=\"true\"] {\n cursor: not-allowed;\n opacity: 0.5;\n}\nbutton:focus-visible,\ntextarea:focus-visible,\ninput:focus-visible,\n.thread:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 1px;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n/* Buttons */\n.ghost {\n border: 1px solid var(--line);\n background: var(--surface);\n border-radius: 5px;\n padding: 2px 8px;\n font-size: 12px;\n min-height: 24px;\n}\n.ghost:hover:not(:disabled):not([aria-disabled=\"true\"]) {\n border-color: var(--accent);\n color: var(--accent);\n}\n.primary {\n border: 1px solid var(--accent);\n background: var(--accent);\n color: var(--accent-text);\n border-radius: 5px;\n padding: 2px 10px;\n font-size: 12.5px;\n font-weight: 600;\n min-height: 24px;\n}\n.danger {\n border: 1px solid var(--removed);\n background: var(--removed);\n color: var(--surface);\n border-radius: 5px;\n padding: 2px 10px;\n font-size: 12.5px;\n font-weight: 600;\n min-height: 24px;\n}\n.link {\n border: 0;\n background: transparent;\n color: var(--accent);\n padding: 0;\n font-size: inherit;\n text-align: inherit;\n}\na.link {\n text-decoration: none;\n}\n.link:hover {\n text-decoration: underline;\n}\n.hint {\n color: var(--muted);\n font-size: 11.5px;\n}\n.refuse,\n.error {\n color: var(--removed);\n font-size: 12px;\n}\n.spacer {\n flex: 1;\n}\n\n/* The layer over the page: the column, markers' panel, dialogs. */\n.layer {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n.layer > *,\n.column > * {\n pointer-events: auto;\n}\n.column {\n position: absolute;\n top: 0;\n width: 250px;\n pointer-events: none;\n}\n.slot {\n position: absolute;\n left: 0;\n right: 0;\n}\n.slot > .thread,\n.slot > .composer {\n margin: 0 0 6px;\n}\n.slot.linked .thread,\n.slot.linked .composer {\n border-color: var(--accent);\n}\n.highlight {\n position: absolute;\n pointer-events: none;\n background: color-mix(in srgb, var(--flash) 55%, transparent);\n border-radius: 3px;\n /* Over the page's text, a tint that keeps the text as it was: multiplied\n on a light page, screened on a dark one. */\n mix-blend-mode: var(--ascribe-review-blend, multiply);\n}\nsvg.connector {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n overflow: visible;\n pointer-events: none;\n}\nsvg.connector path {\n fill: none;\n stroke: var(--accent);\n stroke-width: 1.5;\n}\n\n/* The block tools: the Comment button, and the count marker at narrow widths. */\n.tool {\n border: 1px solid var(--line);\n background: var(--surface);\n border-radius: 5px;\n font-size: 11.5px;\n padding: 0 7px;\n min-height: 22px;\n color: var(--accent);\n white-space: nowrap;\n}\n.hint-count {\n margin-inline-start: 2px;\n font-size: 11px;\n font-weight: 600;\n color: var(--accent);\n}\n\n.marker {\n border: 1px solid var(--accent);\n background: var(--surface);\n color: var(--accent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n padding: 0 8px;\n min-width: 30px;\n min-height: 24px;\n}\n\n/* Threads */\n.thread {\n background: var(--thread);\n border: 1px solid var(--line);\n border-radius: 6px;\n margin: 6px 0 10px;\n}\n.th {\n display: flex;\n flex-wrap: wrap;\n gap: 3px 8px;\n align-items: center;\n padding: 5px 9px 2px;\n font-size: 11.5px;\n color: var(--muted);\n}\n.acts {\n display: flex;\n flex-wrap: wrap;\n gap: 2px 12px;\n padding: 0 9px 5px;\n border-bottom: 1px solid var(--line);\n font-size: 11.5px;\n}\n.thread.collapsed .acts {\n border-bottom: 0;\n}\n.orig,\n.inside {\n padding: 5px 9px;\n border-bottom: 1px solid var(--line);\n font-size: 11.5px;\n color: var(--muted);\n}\n.orig q {\n color: var(--text);\n white-space: pre-wrap;\n}\n.badge {\n border-radius: 999px;\n padding: 0 7px;\n font-size: 10.5px;\n font-weight: 600;\n border: 1px solid currentColor;\n white-space: nowrap;\n}\n.badge.unsent {\n color: var(--changed);\n}\n.badge.resolved {\n color: var(--added);\n}\n.badge.outdated {\n color: var(--muted);\n}\n.badge.summary {\n color: var(--accent);\n}\n.badge.removed,\n.badge.detached {\n color: var(--removed);\n}\n.comment {\n display: grid;\n grid-template-columns: 22px minmax(0, 1fr);\n gap: 7px;\n padding: 6px 9px;\n}\n.comment + .comment {\n border-top: 1px dashed var(--line);\n}\n.avatar {\n width: 22px;\n height: 22px;\n border-radius: 50%;\n background: var(--accent);\n color: var(--accent-text);\n display: grid;\n place-items: center;\n font-size: 9.5px;\n font-weight: 700;\n}\n.who {\n font-weight: 600;\n}\n.when {\n color: var(--muted);\n font-size: 11px;\n margin-left: 6px;\n}\n.body {\n overflow-wrap: anywhere;\n}\n.body > :first-child {\n margin-top: 1px;\n}\n.body > :last-child {\n margin-bottom: 0;\n}\n.body p,\n.body ul,\n.body ol,\n.body blockquote,\n.body pre {\n margin: 4px 0;\n}\n.body ul,\n.body ol {\n padding-left: 18px;\n}\n.body blockquote {\n border-left: 3px solid var(--line);\n padding-left: 8px;\n color: var(--muted);\n}\n.body code {\n font-family: var(--mono);\n font-size: 0.92em;\n background: color-mix(in srgb, var(--muted) 14%, transparent);\n padding: 0.05em 0.3em;\n border-radius: 3px;\n}\n.body pre {\n overflow-x: auto;\n padding: 6px 8px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--muted) 14%, transparent);\n}\n.body pre code {\n background: none;\n padding: 0;\n}\n.body a {\n color: var(--accent);\n}\n.reply {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 6px 9px 8px;\n border-top: 1px solid var(--line);\n}\n.reply textarea {\n flex: 1 1 100%;\n min-width: 80px;\n padding: 2px 8px;\n border: 1px solid var(--line);\n border-radius: 5px;\n background: var(--input);\n resize: vertical;\n field-sizing: content;\n min-height: 26px;\n max-height: 160px;\n}\n.reply .error,\n.reply .hint {\n flex-basis: 100%;\n}\n.composer {\n background: var(--thread);\n border: 1px solid var(--accent);\n border-radius: 6px;\n padding: 8px 10px;\n margin: 6px 0 10px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.composer textarea {\n width: 100%;\n min-height: 56px;\n resize: vertical;\n padding: 6px 8px;\n border: 1px solid var(--line);\n border-radius: 5px;\n background: var(--input);\n}\n.row {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n align-items: center;\n}\n\n/* The detached list, above the page */\n.detached {\n margin: 12px 0;\n border: 1px dashed var(--line);\n border-radius: 6px;\n padding: 8px 10px 2px;\n color: var(--muted);\n max-width: 520px;\n}\n.detached .thread {\n color: var(--text);\n}\n\n/* The unsent bar, at the bottom */\n.unsent-bar {\n background: var(--changed-background);\n border-top: 1px solid var(--line);\n padding: 6px 10px;\n display: flex;\n flex-wrap: wrap;\n gap: 6px 10px;\n align-items: center;\n}\n.unsent-bar .hint {\n font-size: 12px;\n}\n\n/* The panel for one block's threads, at narrow widths */\n.sheet {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n max-height: 62vh;\n overflow: auto;\n background: var(--surface);\n border-top: 2px solid var(--accent);\n box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.22);\n padding: 8px 12px 10px;\n z-index: 4;\n}\n.sheet .head {\n display: flex;\n gap: 8px;\n align-items: center;\n font-size: 12px;\n color: var(--muted);\n}\n\n/* Dialogs */\n.backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.4);\n z-index: 8;\n display: grid;\n place-items: center;\n padding: 12px;\n}\n.dialog {\n background: var(--surface);\n border: 1px solid var(--line);\n border-radius: 8px;\n width: min(480px, 100%);\n max-height: 100%;\n overflow: auto;\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n font-size: 13px;\n}\n.dialog h2 {\n margin: 0;\n font-size: 14px;\n}\n.dialog ul.unsent-list {\n margin: 0;\n padding-left: 18px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.dialog fieldset {\n border: 0;\n padding: 0;\n margin: 0;\n display: flex;\n flex-wrap: wrap;\n gap: 4px 14px;\n}\n.dialog textarea {\n width: 100%;\n min-height: 56px;\n resize: vertical;\n padding: 6px 8px;\n border: 1px solid var(--line);\n border-radius: 5px;\n background: var(--input);\n}\n.confirm {\n border: 1px solid var(--removed);\n background: var(--removed-background);\n border-radius: 6px;\n padding: 8px 10px;\n}\n.segmented {\n display: inline-flex;\n flex-wrap: wrap;\n align-self: flex-start;\n border: 1px solid var(--line);\n border-radius: 5px;\n overflow: hidden;\n}\n.segmented button {\n border: 0;\n background: transparent;\n padding: 2px 9px;\n font-size: 12px;\n color: var(--muted);\n min-height: 24px;\n}\n.segmented button + button {\n border-left: 1px solid var(--line);\n}\n.segmented button[aria-pressed=\"true\"] {\n background: var(--accent);\n color: var(--accent-text);\n}\n.thread-list {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n.thread-list li + li {\n border-top: 1px solid var(--line);\n}\n.thread-list button {\n display: flex;\n flex-direction: column;\n gap: 1px;\n width: 100%;\n text-align: left;\n border: 0;\n background: transparent;\n padding: 6px 4px;\n}\n.thread-list button:hover,\n.thread-list button:focus-visible {\n background: color-mix(in srgb, var(--muted) 12%, transparent);\n}\n.thread-list .where {\n font-size: 11.5px;\n color: var(--muted);\n}\n.thread-list .first {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.flash {\n animation: flash 1.4s ease-out;\n}\n@keyframes flash {\n from {\n box-shadow: 0 0 0 3px var(--accent);\n }\n to {\n box-shadow: 0 0 0 0 transparent;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .flash {\n animation: none;\n outline: 2px solid var(--accent);\n }\n}\n`;\n"]}
1
+ {"version":3,"file":"style.js","sourceRoot":"","sources":["../../src/overlay/style.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,kDAAkD;AAElD,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2jB1B,CAAC","sourcesContent":["// The overlay's stylesheet. It applies inside the overlay's shadow roots only,\n// so the page's styles don't reach the overlay and the overlay's don't reach\n// the page. Colors come from the page's --ascribe-review-* custom properties\n// (`marks.css` sets them, with light and dark values; a host can set its own),\n// which reach inside shadow roots by inheritance.\n\nexport const OVERLAY_CSS = `\n:host {\n all: initial;\n /* Generated from design/tokens.toml by scripts/design/tokens.ts: host, light. */\n --muted: var(--ascribe-review-muted, #676c75);\n --line: var(--ascribe-review-border, #e3e6ec);\n --thread: var(--ascribe-review-thread, #f7f9fd);\n --accent: var(--ascribe-review-focus, #5962e8);\n --accent-text: var(--ascribe-review-accent-text, #ffffff);\n --flash: var(--ascribe-review-flash, #fee9d0);\n --added: var(--ascribe-review-added, #057256);\n --changed: var(--ascribe-review-changed, #845502);\n --changed-background: var(--ascribe-review-changed-background, #fee9d0);\n --removed: var(--ascribe-review-removed, #ca3f02);\n --removed-background: var(--ascribe-review-removed-background, #fef4f1);\n --mono: var(--ascribe-review-mono, ui-monospace, monospace);\n font-family: var(--ascribe-review-font, system-ui, -apple-system, \"Segoe UI\", sans-serif);\n /* End of generated host. */\n --text: var(--ascribe-review-text, CanvasText);\n --surface: var(--ascribe-review-surface, Canvas);\n --input: var(--ascribe-review-input, var(--surface));\n font-size: 12.5px;\n line-height: 1.45;\n color: var(--text);\n}\n* {\n box-sizing: border-box;\n}\n[hidden] {\n display: none !important;\n}\nbutton,\ntextarea,\ninput {\n font: inherit;\n color: inherit;\n}\nbutton {\n cursor: pointer;\n}\nbutton:disabled,\nbutton[aria-disabled=\"true\"] {\n cursor: not-allowed;\n opacity: 0.5;\n}\nbutton:focus-visible,\ntextarea:focus-visible,\ninput:focus-visible,\n.thread:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 1px;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n/* Buttons */\n.ghost {\n border: 1px solid var(--line);\n background: var(--surface);\n border-radius: 5px;\n padding: 2px 8px;\n font-size: 12px;\n min-height: 24px;\n}\n.ghost:hover:not(:disabled):not([aria-disabled=\"true\"]) {\n border-color: var(--accent);\n color: var(--accent);\n}\n.primary {\n border: 1px solid var(--accent);\n background: var(--accent);\n color: var(--accent-text);\n border-radius: 5px;\n padding: 2px 10px;\n font-size: 12.5px;\n font-weight: 600;\n min-height: 24px;\n}\n.danger {\n border: 1px solid var(--removed);\n background: var(--removed);\n color: var(--surface);\n border-radius: 5px;\n padding: 2px 10px;\n font-size: 12.5px;\n font-weight: 600;\n min-height: 24px;\n}\n.link {\n border: 0;\n background: transparent;\n color: var(--accent);\n padding: 0;\n font-size: inherit;\n text-align: inherit;\n}\na.link {\n text-decoration: none;\n}\n.link:hover {\n text-decoration: underline;\n}\n.hint {\n color: var(--muted);\n font-size: 11.5px;\n}\n.refuse,\n.error {\n color: var(--removed);\n font-size: 12px;\n}\n.spacer {\n flex: 1;\n}\n\n/* The layer over the page: the column, markers' panel, dialogs. */\n.layer {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n.layer > *,\n.column > * {\n pointer-events: auto;\n}\n.column {\n position: absolute;\n top: 0;\n width: 250px;\n pointer-events: none;\n}\n.slot {\n position: absolute;\n left: 0;\n right: 0;\n}\n.slot > .thread,\n.slot > .composer {\n margin: 0 0 6px;\n}\n.slot.linked .thread,\n.slot.linked .composer {\n border-color: var(--accent);\n}\n.highlight {\n position: absolute;\n pointer-events: none;\n background: color-mix(in srgb, var(--flash) 55%, transparent);\n border-radius: 3px;\n /* Over the page's text, a tint that keeps the text as it was: multiplied\n on a light page, screened on a dark one. */\n mix-blend-mode: var(--ascribe-review-blend, multiply);\n}\nsvg.connector {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n overflow: visible;\n pointer-events: none;\n}\nsvg.connector path {\n fill: none;\n stroke: var(--accent);\n stroke-width: 1.5;\n}\n\n/* The block tools: the Comment button, and the count marker at narrow widths. */\n.tool {\n border: 1px solid var(--line);\n background: var(--surface);\n border-radius: 5px;\n font-size: 11.5px;\n padding: 0 7px;\n min-height: 22px;\n color: var(--accent);\n white-space: nowrap;\n}\n.hint-count {\n margin-inline-start: 2px;\n font-size: 11px;\n font-weight: 600;\n color: var(--accent);\n}\n\n.marker {\n border: 1px solid var(--accent);\n background: var(--surface);\n color: var(--accent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n padding: 0 8px;\n min-width: 30px;\n min-height: 24px;\n}\n\n/* Threads */\n.thread {\n background: var(--thread);\n border: 1px solid var(--line);\n border-radius: 6px;\n margin: 6px 0 10px;\n}\n.th {\n display: flex;\n flex-wrap: wrap;\n gap: 3px 8px;\n align-items: center;\n padding: 5px 9px 2px;\n font-size: 11.5px;\n color: var(--muted);\n}\n.acts {\n display: flex;\n flex-wrap: wrap;\n gap: 2px 12px;\n padding: 0 9px 5px;\n border-bottom: 1px solid var(--line);\n font-size: 11.5px;\n}\n.thread.collapsed .acts {\n border-bottom: 0;\n}\n.orig,\n.inside {\n padding: 5px 9px;\n border-bottom: 1px solid var(--line);\n font-size: 11.5px;\n color: var(--muted);\n}\n.orig q {\n color: var(--text);\n white-space: pre-wrap;\n}\n.badge {\n border-radius: 999px;\n padding: 0 7px;\n font-size: 10.5px;\n font-weight: 600;\n border: 1px solid currentColor;\n white-space: nowrap;\n}\n.badge.unsent {\n color: var(--changed);\n}\n.badge.resolved {\n color: var(--added);\n}\n.badge.outdated {\n color: var(--muted);\n}\n.badge.summary {\n color: var(--accent);\n}\n.badge.removed,\n.badge.detached {\n color: var(--removed);\n}\n.comment {\n display: grid;\n grid-template-columns: 22px minmax(0, 1fr);\n gap: 7px;\n padding: 6px 9px;\n}\n.comment + .comment {\n border-top: 1px dashed var(--line);\n}\n.avatar {\n width: 22px;\n height: 22px;\n border-radius: 50%;\n background: var(--accent);\n color: var(--accent-text);\n display: grid;\n place-items: center;\n font-size: 9.5px;\n font-weight: 700;\n}\n.who {\n font-weight: 600;\n}\n.when {\n color: var(--muted);\n font-size: 11px;\n margin-left: 6px;\n}\n.body {\n overflow-wrap: anywhere;\n}\n.body > :first-child {\n margin-top: 1px;\n}\n.body > :last-child {\n margin-bottom: 0;\n}\n.body p,\n.body ul,\n.body ol,\n.body blockquote,\n.body pre {\n margin: 4px 0;\n}\n.body ul,\n.body ol {\n padding-left: 18px;\n}\n.body blockquote {\n border-left: 3px solid var(--line);\n padding-left: 8px;\n color: var(--muted);\n}\n.body code,\n.thread-list .where code {\n font-family: var(--mono);\n font-size: 0.92em;\n background: color-mix(in srgb, var(--muted) 14%, transparent);\n padding: 0.05em 0.3em;\n border-radius: 3px;\n}\n.body pre {\n overflow-x: auto;\n padding: 6px 8px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--muted) 14%, transparent);\n}\n.body pre code {\n background: none;\n padding: 0;\n}\n.body a {\n color: var(--accent);\n}\n.reply {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 6px 9px 8px;\n border-top: 1px solid var(--line);\n}\n.reply textarea {\n flex: 1 1 100%;\n min-width: 80px;\n padding: 2px 8px;\n border: 1px solid var(--line);\n border-radius: 5px;\n background: var(--input);\n resize: vertical;\n field-sizing: content;\n min-height: 26px;\n max-height: 160px;\n}\n.reply .error,\n.reply .hint {\n flex-basis: 100%;\n}\n.composer {\n background: var(--thread);\n border: 1px solid var(--accent);\n border-radius: 6px;\n padding: 8px 10px;\n margin: 6px 0 10px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.composer textarea {\n width: 100%;\n min-height: 56px;\n resize: vertical;\n padding: 6px 8px;\n border: 1px solid var(--line);\n border-radius: 5px;\n background: var(--input);\n}\n.row {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n align-items: center;\n}\n\n/* The detached list, above the page */\n.detached {\n margin: 12px 0;\n border: 1px dashed var(--line);\n border-radius: 6px;\n padding: 8px 10px 2px;\n color: var(--muted);\n max-width: 520px;\n}\n.detached .thread {\n color: var(--text);\n}\n\n/* The unsent bar, at the bottom */\n.unsent-bar {\n background: var(--changed-background);\n border-top: 1px solid var(--line);\n padding: 6px 10px;\n display: flex;\n flex-wrap: wrap;\n gap: 6px 10px;\n align-items: center;\n}\n.unsent-bar .hint {\n color: var(--text);\n font-size: 12px;\n}\n\n/* The panel for one block's threads, at narrow widths */\n.sheet {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n max-height: 62vh;\n overflow: auto;\n background: var(--surface);\n border-top: 2px solid var(--accent);\n /* Generated from design/tokens.toml by scripts/design/tokens.ts: sheet, light. */\n box-shadow: 0 -8px 24px rgb(0 0 0 / 0.22);\n /* End of generated sheet. */\n padding: 8px 12px 10px;\n z-index: 4;\n}\n.sheet .head {\n display: flex;\n gap: 8px;\n align-items: center;\n font-size: 12px;\n color: var(--muted);\n}\n\n/* Dialogs */\n.backdrop {\n position: fixed;\n inset: 0;\n /* Generated from design/tokens.toml by scripts/design/tokens.ts: backdrop, light. */\n background: rgb(0 0 0 / 0.4);\n /* End of generated backdrop. */\n z-index: 8;\n display: grid;\n place-items: center;\n padding: 12px;\n}\n.dialog {\n background: var(--surface);\n border: 1px solid var(--line);\n border-radius: 8px;\n width: min(480px, 100%);\n max-height: 100%;\n overflow: auto;\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n font-size: 13px;\n}\n.dialog h2 {\n margin: 0;\n font-size: 14px;\n}\n.dialog ul.unsent-list {\n margin: 0;\n padding-left: 18px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.dialog fieldset {\n border: 0;\n padding: 0;\n margin: 0;\n display: flex;\n flex-wrap: wrap;\n gap: 4px 14px;\n}\n.dialog textarea {\n width: 100%;\n min-height: 56px;\n resize: vertical;\n padding: 6px 8px;\n border: 1px solid var(--line);\n border-radius: 5px;\n background: var(--input);\n}\n.confirm {\n border: 1px solid var(--removed);\n background: var(--removed-background);\n border-radius: 6px;\n padding: 8px 10px;\n}\n.segmented {\n display: inline-flex;\n flex-wrap: wrap;\n align-self: flex-start;\n border: 1px solid var(--line);\n border-radius: 5px;\n overflow: hidden;\n}\n.segmented button {\n border: 0;\n background: transparent;\n padding: 2px 9px;\n font-size: 12px;\n color: var(--muted);\n min-height: 24px;\n}\n.segmented button + button {\n border-left: 1px solid var(--line);\n}\n.segmented button[aria-pressed=\"true\"] {\n background: var(--accent);\n color: var(--accent-text);\n}\n.thread-list {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n.thread-list li + li {\n border-top: 1px solid var(--line);\n}\n.thread-list button {\n display: flex;\n flex-direction: column;\n gap: 1px;\n width: 100%;\n text-align: left;\n border: 0;\n background: transparent;\n padding: 6px 4px;\n}\n.thread-list button:hover,\n.thread-list button:focus-visible {\n background: color-mix(in srgb, var(--muted) 12%, transparent);\n}\n.thread-list .where {\n font-size: 11.5px;\n color: var(--muted);\n}\n.thread-list .first {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.flash {\n animation: flash 1.4s ease-out;\n}\n@keyframes flash {\n from {\n box-shadow: 0 0 0 3px var(--accent);\n }\n to {\n box-shadow: 0 0 0 0 transparent;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .flash {\n animation: none;\n outline: 2px solid var(--accent);\n }\n}\n`;\n"]}
@@ -1,3 +1,4 @@
1
+ import { DATA_OVERLAY, DATA_UI } from "../names.js";
1
2
  /** `"1 comment"`, `"2 comments"`. */
2
3
  export function plural(count, one, many = `${one}s`) {
3
4
  return `${count} ${count === 1 ? one : many}`;
@@ -163,8 +164,8 @@ export function blockText(block) {
163
164
  if (!(node instanceof Element))
164
165
  return;
165
166
  const tag = node.tagName.toUpperCase();
166
- if (node.hasAttribute("data-ascribe-overlay") ||
167
- (node.hasAttribute("data-ascribe-ui") && tag !== "INS") ||
167
+ if (node.hasAttribute(DATA_OVERLAY) ||
168
+ (node.hasAttribute(DATA_UI) && tag !== "INS") ||
168
169
  ["SCRIPT", "STYLE", "TEMPLATE", "NOSCRIPT"].includes(tag) ||
169
170
  unseen(node)) {
170
171
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/overlay/text.ts"],"names":[],"mappings":"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","sourcesContent":["// What the overlay says about threads: where each one is, when a comment was\n// made, and counts in words.\nimport type { LocatedThread } from \"../place/place.js\";\nimport type { Author } from \"../shared/types.js\";\n\n/** `\"1 comment\"`, `\"2 comments\"`. */\nexport function plural(count: number, one: string, many = `${one}s`): string {\n return `${count} ${count === 1 ? one : many}`;\n}\n\n/** The last segment of a content path. */\nfunction fileName(path: string): string {\n return path.slice(path.lastIndexOf(\"/\") + 1);\n}\n\n/** `rollouts.md:28`, or `rollouts.md:28-30`. */\nexport function linesLabel(path: string, lines: { first: number; last: number }): string {\n const range = lines.first === lines.last ? `${lines.first}` : `${lines.first}-${lines.last}`;\n return `${fileName(path)}:${range}`;\n}\n\n/** Where a thread is, for its card's header. */\nexport function whereLabel(thread: LocatedThread): string {\n const file = fileName(thread.path);\n if (thread.detached === \"file\") return `On ${file} as a whole`;\n if (thread.detached === \"line-gone\") {\n const line = thread.originalLine ?? thread.line;\n const commit = thread.originalCommit?.slice(0, 7);\n if (line === null) return `Was on ${file}, since removed`;\n return `Was on ${file}, line ${line}${commit ? ` at ${commit}` : \"\"}, since removed`;\n }\n if (thread.lines === undefined) return file;\n if (thread.detached === \"no-block\")\n return `${linesLabel(thread.path, thread.lines)}, not on this page`;\n if (thread.kind === \"review\" && thread.side === \"LEFT\") return file;\n return linesLabel(thread.path, thread.lines);\n}\n\n/** Where a thread is, for a list of them: shorter, and naming removed text. */\nexport function listLabel(thread: LocatedThread): string {\n const file = fileName(thread.path);\n if (thread.detached !== undefined) return `no block, was ${file}`;\n if (thread.kind === \"review\" && thread.side === \"LEFT\") return `removed text in ${file}`;\n return thread.lines === undefined ? file : linesLabel(thread.path, thread.lines);\n}\n\n/** Whether the thread is on removed text. */\nexport function onRemovedText(thread: LocatedThread): boolean {\n return thread.kind === \"review\" && thread.side === \"LEFT\";\n}\n\n/** Whether any comment of the thread is unsent. */\nexport function hasUnsent(thread: LocatedThread): boolean {\n return thread.comments.some((comment) => comment.pending);\n}\n\n/** A comment's author's name, `\"you\"` for the viewer. */\nexport function authorName(author: Author | null, viewer: string): string {\n if (author === null) return \"ghost\";\n return author.login === viewer ? \"you\" : author.login;\n}\n\n/** Two letters standing for an author, for the avatar's place. */\nexport function initials(author: Author | null): string {\n return (\n (author?.login ?? \"?\")\n .replace(/[^A-Za-z0-9]/g, \"\")\n .slice(0, 2)\n .toUpperCase() || \"?\"\n );\n}\n\n/** `\"just now\"`, `\"5 minutes ago\"`, `\"yesterday\"`, or a date. */\nexport function relativeTime(iso: string, now: number): string {\n const then = Date.parse(iso);\n if (Number.isNaN(then)) return \"\";\n const seconds = Math.max(0, Math.round((now - then) / 1000));\n if (seconds < 60) return \"just now\";\n const minutes = Math.round(seconds / 60);\n if (minutes < 60) return `${plural(minutes, \"minute\")} ago`;\n const hours = Math.round(minutes / 60);\n if (hours < 24) return `${plural(hours, \"hour\")} ago`;\n const days = Math.round(hours / 24);\n if (days === 1) return \"yesterday\";\n if (days < 30) return `${days} days ago`;\n return new Date(then).toISOString().slice(0, 10);\n}\n\n/** Elements whose text starts on a line of its own. */\nconst BLOCK_TAGS = new Set([\n \"ADDRESS\",\n \"ARTICLE\",\n \"ASIDE\",\n \"BLOCKQUOTE\",\n \"DD\",\n \"DETAILS\",\n \"DIV\",\n \"DL\",\n \"DT\",\n \"FIGCAPTION\",\n \"FIGURE\",\n \"FOOTER\",\n \"H1\",\n \"H2\",\n \"H3\",\n \"H4\",\n \"H5\",\n \"H6\",\n \"HEADER\",\n \"HR\",\n \"LI\",\n \"OL\",\n \"P\",\n \"PRE\",\n \"SECTION\",\n \"SUMMARY\",\n \"TABLE\",\n \"TR\",\n \"UL\",\n]);\n\n/** Classes that hide text from sight but not from screen readers. */\nconst SCREEN_READER_ONLY = [\"sr-only\", \"visually-hidden\", \"sl-sr-only\"];\n\n/** Whether a reader can't see `el`. */\nfunction unseen(el: Element): boolean {\n if (\n el.hasAttribute(\"hidden\") ||\n el.getAttribute(\"aria-hidden\") === \"true\" ||\n SCREEN_READER_ONLY.some((name) => el.classList.contains(name))\n ) {\n return true;\n }\n const style = el.ownerDocument.defaultView?.getComputedStyle(el);\n return style?.display === \"none\" || style?.visibility === \"hidden\";\n}\n\n/**\n * A block's text as a reader sees it, for quoting: links as their text, a\n * line for each paragraph, list item, or row, numbered items with their\n * numbers, images as their alt text, and code as written. What the marks and\n * the overlay add is left out, but for the words a change inserted, and so\n * is anything a reader can't see.\n */\nexport function blockText(block: Element): string {\n // Each line, and whether it's code, which keeps its spaces.\n const lines: { text: string; pre: boolean }[] = [];\n let line = \"\";\n let linePre = false;\n const breakLine = (): void => {\n if (line.trim() !== \"\") lines.push({ text: line, pre: linePre });\n line = \"\";\n linePre = false;\n };\n const walk = (node: Node, pre: boolean): void => {\n if (node.nodeType === 3) {\n const text = node.nodeValue ?? \"\";\n if (!pre) {\n line += text.replace(/\\s+/g, \" \");\n return;\n }\n const [first = \"\", ...rest] = text.split(\"\\n\");\n line += first;\n linePre = true;\n for (const next of rest) {\n lines.push({ text: line, pre: true });\n line = next;\n }\n return;\n }\n if (!(node instanceof Element)) return;\n const tag = node.tagName.toUpperCase();\n if (\n node.hasAttribute(\"data-ascribe-overlay\") ||\n (node.hasAttribute(\"data-ascribe-ui\") && tag !== \"INS\") ||\n [\"SCRIPT\", \"STYLE\", \"TEMPLATE\", \"NOSCRIPT\"].includes(tag) ||\n unseen(node)\n ) {\n return;\n }\n if (tag === \"BR\") {\n breakLine();\n return;\n }\n if (tag === \"IMG\") {\n line += node.getAttribute(\"alt\") ?? \"\";\n return;\n }\n const block = BLOCK_TAGS.has(tag);\n if (block) breakLine();\n if ((tag === \"TD\" || tag === \"TH\") && node.previousElementSibling !== null) line += \" | \";\n if (tag === \"LI\" && node.parentElement?.tagName.toUpperCase() === \"OL\") {\n const list = node.parentElement;\n const start = Number(list.getAttribute(\"start\") ?? \"1\");\n const items = Array.from(list.children).filter((c) => c.tagName.toUpperCase() === \"LI\");\n line += `${(Number.isFinite(start) ? start : 1) + items.indexOf(node)}. `;\n }\n for (const child of Array.from(node.childNodes)) walk(child, pre || tag === \"PRE\");\n if (block) breakLine();\n };\n walk(block, false);\n breakLine();\n return lines.map(({ text, pre }) => (pre ? text.trimEnd() : text.trim())).join(\"\\n\");\n}\n"]}
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/overlay/text.ts"],"names":[],"mappings":"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","sourcesContent":["// What the overlay says about threads: where each one is, when a comment was\n// made, and counts in words.\nimport type { LocatedThread } from \"../place/place.js\";\nimport type { Author } from \"../shared/types.js\";\nimport { DATA_OVERLAY, DATA_UI } from \"../names.js\";\n\n/** `\"1 comment\"`, `\"2 comments\"`. */\nexport function plural(count: number, one: string, many = `${one}s`): string {\n return `${count} ${count === 1 ? one : many}`;\n}\n\n/** The last segment of a content path. */\nfunction fileName(path: string): string {\n return path.slice(path.lastIndexOf(\"/\") + 1);\n}\n\n/** `rollouts.md:28`, or `rollouts.md:28-30`. */\nexport function linesLabel(path: string, lines: { first: number; last: number }): string {\n const range = lines.first === lines.last ? `${lines.first}` : `${lines.first}-${lines.last}`;\n return `${fileName(path)}:${range}`;\n}\n\n/** Where a thread is, for its card's header. */\nexport function whereLabel(thread: LocatedThread): string {\n const file = fileName(thread.path);\n if (thread.detached === \"file\") return `On ${file} as a whole`;\n if (thread.detached === \"line-gone\") {\n const line = thread.originalLine ?? thread.line;\n const commit = thread.originalCommit?.slice(0, 7);\n if (line === null) return `Was on ${file}, since removed`;\n return `Was on ${file}, line ${line}${commit ? ` at ${commit}` : \"\"}, since removed`;\n }\n if (thread.lines === undefined) return file;\n if (thread.detached === \"no-block\")\n return `${linesLabel(thread.path, thread.lines)}, not on this page`;\n if (thread.kind === \"review\" && thread.side === \"LEFT\") return file;\n return linesLabel(thread.path, thread.lines);\n}\n\n/** Where a thread is, for a list of them: shorter, and naming removed text. */\nexport function listLabel(thread: LocatedThread): string {\n const file = fileName(thread.path);\n if (thread.detached !== undefined) return `no block, was ${file}`;\n if (thread.kind === \"review\" && thread.side === \"LEFT\") return `removed text in ${file}`;\n return thread.lines === undefined ? file : linesLabel(thread.path, thread.lines);\n}\n\n/** Whether the thread is on removed text. */\nexport function onRemovedText(thread: LocatedThread): boolean {\n return thread.kind === \"review\" && thread.side === \"LEFT\";\n}\n\n/** Whether any comment of the thread is unsent. */\nexport function hasUnsent(thread: LocatedThread): boolean {\n return thread.comments.some((comment) => comment.pending);\n}\n\n/** A comment's author's name, `\"you\"` for the viewer. */\nexport function authorName(author: Author | null, viewer: string): string {\n if (author === null) return \"ghost\";\n return author.login === viewer ? \"you\" : author.login;\n}\n\n/** Two letters standing for an author, for the avatar's place. */\nexport function initials(author: Author | null): string {\n return (\n (author?.login ?? \"?\")\n .replace(/[^A-Za-z0-9]/g, \"\")\n .slice(0, 2)\n .toUpperCase() || \"?\"\n );\n}\n\n/** `\"just now\"`, `\"5 minutes ago\"`, `\"yesterday\"`, or a date. */\nexport function relativeTime(iso: string, now: number): string {\n const then = Date.parse(iso);\n if (Number.isNaN(then)) return \"\";\n const seconds = Math.max(0, Math.round((now - then) / 1000));\n if (seconds < 60) return \"just now\";\n const minutes = Math.round(seconds / 60);\n if (minutes < 60) return `${plural(minutes, \"minute\")} ago`;\n const hours = Math.round(minutes / 60);\n if (hours < 24) return `${plural(hours, \"hour\")} ago`;\n const days = Math.round(hours / 24);\n if (days === 1) return \"yesterday\";\n if (days < 30) return `${days} days ago`;\n return new Date(then).toISOString().slice(0, 10);\n}\n\n/** Elements whose text starts on a line of its own. */\nconst BLOCK_TAGS = new Set([\n \"ADDRESS\",\n \"ARTICLE\",\n \"ASIDE\",\n \"BLOCKQUOTE\",\n \"DD\",\n \"DETAILS\",\n \"DIV\",\n \"DL\",\n \"DT\",\n \"FIGCAPTION\",\n \"FIGURE\",\n \"FOOTER\",\n \"H1\",\n \"H2\",\n \"H3\",\n \"H4\",\n \"H5\",\n \"H6\",\n \"HEADER\",\n \"HR\",\n \"LI\",\n \"OL\",\n \"P\",\n \"PRE\",\n \"SECTION\",\n \"SUMMARY\",\n \"TABLE\",\n \"TR\",\n \"UL\",\n]);\n\n/** Classes that hide text from sight but not from screen readers. */\nconst SCREEN_READER_ONLY = [\"sr-only\", \"visually-hidden\", \"sl-sr-only\"];\n\n/** Whether a reader can't see `el`. */\nfunction unseen(el: Element): boolean {\n if (\n el.hasAttribute(\"hidden\") ||\n el.getAttribute(\"aria-hidden\") === \"true\" ||\n SCREEN_READER_ONLY.some((name) => el.classList.contains(name))\n ) {\n return true;\n }\n const style = el.ownerDocument.defaultView?.getComputedStyle(el);\n return style?.display === \"none\" || style?.visibility === \"hidden\";\n}\n\n/**\n * A block's text as a reader sees it, for quoting: links as their text, a\n * line for each paragraph, list item, or row, numbered items with their\n * numbers, images as their alt text, and code as written. What the marks and\n * the overlay add is left out, but for the words a change inserted, and so\n * is anything a reader can't see.\n */\nexport function blockText(block: Element): string {\n // Each line, and whether it's code, which keeps its spaces.\n const lines: { text: string; pre: boolean }[] = [];\n let line = \"\";\n let linePre = false;\n const breakLine = (): void => {\n if (line.trim() !== \"\") lines.push({ text: line, pre: linePre });\n line = \"\";\n linePre = false;\n };\n const walk = (node: Node, pre: boolean): void => {\n if (node.nodeType === 3) {\n const text = node.nodeValue ?? \"\";\n if (!pre) {\n line += text.replace(/\\s+/g, \" \");\n return;\n }\n const [first = \"\", ...rest] = text.split(\"\\n\");\n line += first;\n linePre = true;\n for (const next of rest) {\n lines.push({ text: line, pre: true });\n line = next;\n }\n return;\n }\n if (!(node instanceof Element)) return;\n const tag = node.tagName.toUpperCase();\n if (\n node.hasAttribute(DATA_OVERLAY) ||\n (node.hasAttribute(DATA_UI) && tag !== \"INS\") ||\n [\"SCRIPT\", \"STYLE\", \"TEMPLATE\", \"NOSCRIPT\"].includes(tag) ||\n unseen(node)\n ) {\n return;\n }\n if (tag === \"BR\") {\n breakLine();\n return;\n }\n if (tag === \"IMG\") {\n line += node.getAttribute(\"alt\") ?? \"\";\n return;\n }\n const block = BLOCK_TAGS.has(tag);\n if (block) breakLine();\n if ((tag === \"TD\" || tag === \"TH\") && node.previousElementSibling !== null) line += \" | \";\n if (tag === \"LI\" && node.parentElement?.tagName.toUpperCase() === \"OL\") {\n const list = node.parentElement;\n const start = Number(list.getAttribute(\"start\") ?? \"1\");\n const items = Array.from(list.children).filter((c) => c.tagName.toUpperCase() === \"LI\");\n line += `${(Number.isFinite(start) ? start : 1) + items.indexOf(node)}. `;\n }\n for (const child of Array.from(node.childNodes)) walk(child, pre || tag === \"PRE\");\n if (block) breakLine();\n };\n walk(block, false);\n breakLine();\n return lines.map(({ text, pre }) => (pre ? text.trimEnd() : text.trim())).join(\"\\n\");\n}\n"]}
@@ -1,8 +1,9 @@
1
1
  import type { CommentTarget, PendingReview, ReviewEvent } from "../github/session.js";
2
2
  import type { Anchor } from "../place/anchor.js";
3
3
  import type { LocatedThread, PlacedThreads } from "../place/place.js";
4
+ import type { FormattedPiece } from "../shapes.js";
4
5
  import type { Thread, ThreadComment } from "../shared/types.js";
5
- export type { Anchor, CommentTarget, LocatedThread, PendingReview, PlacedThreads, ReviewEvent };
6
+ export type { Anchor, CommentTarget, FormattedPiece, LocatedThread, PendingReview, PlacedThreads, ReviewEvent, };
6
7
  /** What the overlay draws for the page it's on. */
7
8
  export interface OverlayData {
8
9
  /** The pull request: its number names it ("#128"). */
@@ -20,10 +21,15 @@ export interface OverlayData {
20
21
  /** A thread on the pull request, and the pages it's on. */
21
22
  export interface ThreadSummary {
22
23
  thread: LocatedThread;
23
- /** The pages that show it, by content path; empty when no page is known to. */
24
+ /**
25
+ * The pages that show it, by content path; empty when no page is known to.
26
+ * `formatted_title` is the title formatted, when its field sets `inline`;
27
+ * a host that doesn't know it leaves it out.
28
+ */
24
29
  pages: {
25
30
  path: string;
26
31
  title: string | null;
32
+ formatted_title?: FormattedPiece[] | null;
27
33
  }[];
28
34
  }
29
35
  /** A failure the host reports: `message` is a sentence to show; `code` is a `ReviewError` code. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/overlay/types.ts"],"names":[],"mappings":"","sourcesContent":["// What the overlay gets from its host, and what it gives back. The types are\n// shared with `./github` and `./place`, but only as types: nothing of them\n// runs in the browser.\nimport type { CommentTarget, PendingReview, ReviewEvent } from \"../github/session.js\";\nimport type { Anchor } from \"../place/anchor.js\";\nimport type { LocatedThread, PlacedThreads } from \"../place/place.js\";\nimport type { Thread, ThreadComment } from \"../shared/types.js\";\n\nexport type { Anchor, CommentTarget, LocatedThread, PendingReview, PlacedThreads, ReviewEvent };\n\n/** What the overlay draws for the page it's on. */\nexport interface OverlayData {\n /** The pull request: its number names it (\"#128\"). */\n pullRequest: { number: number; url: string };\n /** The page's threads, placed on its blocks (`ReviewSession.threads`). */\n threads: PlacedThreads;\n /** The viewer's pending review: what's unsent. */\n pending: PendingReview;\n /** The signed-in viewer's login. */\n viewer: string;\n}\n\n/** A thread on the pull request, and the pages it's on. */\nexport interface ThreadSummary {\n thread: LocatedThread;\n /** The pages that show it, by content path; empty when no page is known to. */\n pages: { path: string; title: string | null }[];\n}\n\n/** A failure the host reports: `message` is a sentence to show; `code` is a `ReviewError` code. */\nexport interface HostError {\n message: string;\n code?: string;\n}\n\n/**\n * Where the overlay gets its data and sends what the reviewer does. A host\n * keeps GitHub to itself: the overlay never sees a token or makes a request.\n */\nexport interface OverlayHost {\n load(): Promise<OverlayData>;\n /** How a comment on the block would go: as a thread, in the review's summary, or not yet. */\n commentTarget(anchor: Anchor): Promise<CommentTarget>;\n /** Comments on a block. `quote` is the block's text as the page shows it. */\n comment(anchor: Anchor, body: string, quote?: string): Promise<Thread>;\n reply(threadId: string, body: string, when: \"now\" | \"withReview\"): Promise<ThreadComment>;\n /** Every thread on the pull request's pages, with the pages each is on. */\n allThreads(): Promise<ThreadSummary[]>;\n resolve(threadId: string, resolved: boolean): Promise<void>;\n submit(event: ReviewEvent, body?: string): Promise<void>;\n /** Deletes the pending review and everything in it. */\n discard(): Promise<void>;\n openSource(anchor: Anchor): void;\n /** Shows a thread that's on another page: the host opens that page, and its overlay goes to the thread. */\n openThread(threadId: string, page: string): void;\n /** Opens a link from a comment. Without it, the link opens as the page would open it. */\n openLink?(url: string): void;\n /** Tells the reviewer something that happened, as the host shows notices. */\n notify?(message: string): void;\n /** Calls `listener` when the page re-rendered or the threads changed. */\n onDidChange(listener: () => void): void;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/overlay/types.ts"],"names":[],"mappings":"","sourcesContent":["// What the overlay gets from its host, and what it gives back. The types are\n// shared with `./github` and `./place`, but only as types: nothing of them\n// runs in the browser.\nimport type { CommentTarget, PendingReview, ReviewEvent } from \"../github/session.js\";\nimport type { Anchor } from \"../place/anchor.js\";\nimport type { LocatedThread, PlacedThreads } from \"../place/place.js\";\nimport type { FormattedPiece } from \"../shapes.js\";\nimport type { Thread, ThreadComment } from \"../shared/types.js\";\n\nexport type {\n Anchor,\n CommentTarget,\n FormattedPiece,\n LocatedThread,\n PendingReview,\n PlacedThreads,\n ReviewEvent,\n};\n\n/** What the overlay draws for the page it's on. */\nexport interface OverlayData {\n /** The pull request: its number names it (\"#128\"). */\n pullRequest: { number: number; url: string };\n /** The page's threads, placed on its blocks (`ReviewSession.threads`). */\n threads: PlacedThreads;\n /** The viewer's pending review: what's unsent. */\n pending: PendingReview;\n /** The signed-in viewer's login. */\n viewer: string;\n}\n\n/** A thread on the pull request, and the pages it's on. */\nexport interface ThreadSummary {\n thread: LocatedThread;\n /**\n * The pages that show it, by content path; empty when no page is known to.\n * `formatted_title` is the title formatted, when its field sets `inline`;\n * a host that doesn't know it leaves it out.\n */\n pages: { path: string; title: string | null; formatted_title?: FormattedPiece[] | null }[];\n}\n\n/** A failure the host reports: `message` is a sentence to show; `code` is a `ReviewError` code. */\nexport interface HostError {\n message: string;\n code?: string;\n}\n\n/**\n * Where the overlay gets its data and sends what the reviewer does. A host\n * keeps GitHub to itself: the overlay never sees a token or makes a request.\n */\nexport interface OverlayHost {\n load(): Promise<OverlayData>;\n /** How a comment on the block would go: as a thread, in the review's summary, or not yet. */\n commentTarget(anchor: Anchor): Promise<CommentTarget>;\n /** Comments on a block. `quote` is the block's text as the page shows it. */\n comment(anchor: Anchor, body: string, quote?: string): Promise<Thread>;\n reply(threadId: string, body: string, when: \"now\" | \"withReview\"): Promise<ThreadComment>;\n /** Every thread on the pull request's pages, with the pages each is on. */\n allThreads(): Promise<ThreadSummary[]>;\n resolve(threadId: string, resolved: boolean): Promise<void>;\n submit(event: ReviewEvent, body?: string): Promise<void>;\n /** Deletes the pending review and everything in it. */\n discard(): Promise<void>;\n openSource(anchor: Anchor): void;\n /** Shows a thread that's on another page: the host opens that page, and its overlay goes to the thread. */\n openThread(threadId: string, page: string): void;\n /** Opens a link from a comment. Without it, the link opens as the page would open it. */\n openLink?(url: string): void;\n /** Tells the reviewer something that happened, as the host shows notices. */\n notify?(message: string): void;\n /** Calls `listener` when the page re-rendered or the threads changed. */\n onDidChange(listener: () => void): void;\n}\n"]}
@@ -1,10 +1,6 @@
1
- /** Where a rendered block came from: its `data-ascribe-source` and `data-ascribe-via`. */
2
- export interface Anchor {
3
- /** `<path>:<first>-<last>`. */
4
- source: string;
5
- /** The includes it came through, outermost first, each `<path>:<line>`. */
6
- via: string[];
7
- }
1
+ import type { Anchor } from "../shapes.js";
2
+ /** Where a rendered block came from: its `data-ascribe-source` and `data-ascribe-via`, as `ascribe diff` writes them. */
3
+ export type { Anchor };
8
4
  /** A block's source, parsed. `path` is decoded. */
9
5
  export interface SourceRange {
10
6
  path: string;
@@ -1 +1 @@
1
- {"version":3,"file":"anchor.js","sourceRoot":"","sources":["../../src/place/anchor.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,mEAAmE;AACnE,4EAA4E;AAiB5E,8EAA8E;AAC9E,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5D,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,KAAK,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK;QAAE,OAAO,SAAS,CAAC;IAChD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;AAClE,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI;SACR,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACf,KAAK,CAAC,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE;QACrD,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACvC,OAAO,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;YAClC,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IAC7D,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CACZ;SACA,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,CAAC","sourcesContent":["// Source anchors: `<path>:<first>-<last>` for a block, `<path>:<line>` for an\n// include. The path is a content path, `/`-separated, each segment\n// percent-encoded except for ASCII letters, digits, `-`, `.`, `_`, and `~`.\n\n/** Where a rendered block came from: its `data-ascribe-source` and `data-ascribe-via`. */\nexport interface Anchor {\n /** `<path>:<first>-<last>`. */\n source: string;\n /** The includes it came through, outermost first, each `<path>:<line>`. */\n via: string[];\n}\n\n/** A block's source, parsed. `path` is decoded. */\nexport interface SourceRange {\n path: string;\n first: number;\n last: number;\n}\n\n/** Parses `<path>:<first>-<last>`, or returns `undefined` if it isn't one. */\nexport function parseSource(source: string): SourceRange | undefined {\n const colon = source.lastIndexOf(\":\");\n if (colon <= 0) return undefined;\n const match = /^(\\d+)-(\\d+)$/.exec(source.slice(colon + 1));\n if (match === null) return undefined;\n const first = Number(match[1]);\n const last = Number(match[2]);\n if (first < 1 || last < first) return undefined;\n let path: string;\n try {\n path = decodePath(source.slice(0, colon));\n } catch {\n return undefined;\n }\n return { path, first, last };\n}\n\n/** Writes a block's source for a decoded content path. */\nexport function formatSource(range: SourceRange): string {\n return `${encodePath(range.path)}:${range.first}-${range.last}`;\n}\n\n/** Percent-encodes each segment of a `/`-separated path, as anchors do. */\nexport function encodePath(path: string): string {\n return path\n .split(\"/\")\n .map((segment) =>\n Array.from(new TextEncoder().encode(segment), (byte) => {\n const char = String.fromCharCode(byte);\n return /[A-Za-z0-9\\-._~]/.test(char)\n ? char\n : `%${byte.toString(16).toUpperCase().padStart(2, \"0\")}`;\n }).join(\"\"),\n )\n .join(\"/\");\n}\n\n/** The inverse of `encodePath`. */\nexport function decodePath(path: string): string {\n return path.split(\"/\").map(decodeURIComponent).join(\"/\");\n}\n\n/** A string that identifies an anchor, `via` included. */\nexport function anchorKey(anchor: Anchor): string {\n return [anchor.source, ...anchor.via].join(\" \");\n}\n"]}
1
+ {"version":3,"file":"anchor.js","sourceRoot":"","sources":["../../src/place/anchor.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,mEAAmE;AACnE,4EAA4E;AAc5E,8EAA8E;AAC9E,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5D,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,KAAK,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK;QAAE,OAAO,SAAS,CAAC;IAChD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;AAClE,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI;SACR,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACf,KAAK,CAAC,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE;QACrD,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACvC,OAAO,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;YAClC,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IAC7D,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CACZ;SACA,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,CAAC","sourcesContent":["// Source anchors: `<path>:<first>-<last>` for a block, `<path>:<line>` for an\n// include. The path is a content path, `/`-separated, each segment\n// percent-encoded except for ASCII letters, digits, `-`, `.`, `_`, and `~`.\n\nimport type { Anchor } from \"../shapes.js\";\n\n/** Where a rendered block came from: its `data-ascribe-source` and `data-ascribe-via`, as `ascribe diff` writes them. */\nexport type { Anchor };\n\n/** A block's source, parsed. `path` is decoded. */\nexport interface SourceRange {\n path: string;\n first: number;\n last: number;\n}\n\n/** Parses `<path>:<first>-<last>`, or returns `undefined` if it isn't one. */\nexport function parseSource(source: string): SourceRange | undefined {\n const colon = source.lastIndexOf(\":\");\n if (colon <= 0) return undefined;\n const match = /^(\\d+)-(\\d+)$/.exec(source.slice(colon + 1));\n if (match === null) return undefined;\n const first = Number(match[1]);\n const last = Number(match[2]);\n if (first < 1 || last < first) return undefined;\n let path: string;\n try {\n path = decodePath(source.slice(0, colon));\n } catch {\n return undefined;\n }\n return { path, first, last };\n}\n\n/** Writes a block's source for a decoded content path. */\nexport function formatSource(range: SourceRange): string {\n return `${encodePath(range.path)}:${range.first}-${range.last}`;\n}\n\n/** Percent-encodes each segment of a `/`-separated path, as anchors do. */\nexport function encodePath(path: string): string {\n return path\n .split(\"/\")\n .map((segment) =>\n Array.from(new TextEncoder().encode(segment), (byte) => {\n const char = String.fromCharCode(byte);\n return /[A-Za-z0-9\\-._~]/.test(char)\n ? char\n : `%${byte.toString(16).toUpperCase().padStart(2, \"0\")}`;\n }).join(\"\"),\n )\n .join(\"/\");\n}\n\n/** The inverse of `encodePath`. */\nexport function decodePath(path: string): string {\n return path.split(\"/\").map(decodeURIComponent).join(\"/\");\n}\n\n/** A string that identifies an anchor, `via` included. */\nexport function anchorKey(anchor: Anchor): string {\n return [anchor.source, ...anchor.via].join(\" \");\n}\n"]}