@aparte/plugin-diff 0.0.0-stage → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/highlight.ts","../src/diff.css?raw","../src/styles.ts","../src/words.ts","../src/aparte-diff.ts"],"sourcesContent":["/**\n * Colour a file's lines with the chat's highlighter, when one is installed.\n *\n * A highlighter colours a whole text, because what a line means depends on the lines above\n * it (a comment opened two lines up, a string that runs on). So each side of the change is\n * highlighted once, as one text, and cut back into lines: Shiki and the highlighters that\n * follow it wrap each line in `<span class=\"line\">`. A highlighter that does not, or a\n * language it cannot read, leaves the lines as plain text.\n */\nimport type { AparteConfig } from '@aparte/core';\n\n/** Extensions whose language goes by another name in a highlighter. */\nconst BY_EXTENSION: Record<string, string> = {\n ts: 'typescript', mts: 'typescript', cts: 'typescript', tsx: 'tsx',\n js: 'javascript', mjs: 'javascript', cjs: 'javascript', jsx: 'jsx',\n md: 'markdown', markdown: 'markdown', mdx: 'mdx',\n py: 'python', rb: 'ruby', rs: 'rust', kt: 'kotlin', kts: 'kotlin',\n h: 'c', hpp: 'cpp', cc: 'cpp', cxx: 'cpp', cs: 'csharp',\n sh: 'shellscript', bash: 'shellscript', zsh: 'shellscript', ps1: 'powershell',\n yml: 'yaml', htm: 'html', svg: 'xml', patch: 'diff',\n};\n\n/** Files known by their name rather than an extension. */\nconst BY_NAME: Record<string, string> = { dockerfile: 'dockerfile', makefile: 'make' };\n\n/** The language of a file, from its path: `src/parse.ts` → `typescript`; `''` when there is none to read. */\nexport function languageOfPath(path: string): string {\n const name = path.slice(path.lastIndexOf('/') + 1).toLowerCase();\n if (BY_NAME[name]) return BY_NAME[name]!;\n const dot = name.lastIndexOf('.');\n if (dot <= 0) return '';\n const extension = name.slice(dot + 1);\n return BY_EXTENSION[extension] ?? extension;\n}\n\n/** A side's lines as the highlighter coloured them, and the classes it put on its block. */\nexport interface HighlightedLines {\n /** One HTML string per line. */\n lines: string[];\n /**\n * The classes of the highlighter's `<pre>`: its stylesheet colours the tokens under them\n * (Shiki's light/dark pair reads `.shiki span`), so each line wears them too.\n */\n className: string;\n}\n\n/**\n * `lines` coloured as one text, cut back into one HTML string per line; `null` when there is\n * no highlighter, or what it returned cannot be cut line for line. The HTML went through the\n * config's sanitizer (`highlightCode` runs it).\n */\nexport async function highlightLines(config: AparteConfig, lines: readonly string[], language: string): Promise<HighlightedLines | null> {\n if (!lines.length || !config.hasHighlightProvider() || typeof document === 'undefined') return null;\n let html: string;\n try {\n html = await config.highlightCode(lines.join('\\n'), language || 'text', { fallback: false });\n } catch {\n return null;\n }\n const box = document.createElement('div');\n box.innerHTML = html; // safe-text: highlightCode returns the config sanitizer's output\n const cut = box.querySelectorAll('.line');\n if (cut.length !== lines.length) return null;\n return { lines: [...cut].map((line) => line.innerHTML), className: box.querySelector('pre')?.className.trim() ?? '' };\n}\n","export default \"/*\\n * @aparte/plugin-diff — a change, line by line.\\n *\\n * Only what is the plugin's own: the file's frame and header, the grid of lines, the folds,\\n * the comments. The buttons are core's `aparte-btn`, the field core's `aparte-field`, the\\n * tags core's `aparte-badge`; this sheet places them and draws none of them.\\n *\\n * The lines are one grid per file — old number, new number, marker, code — so the numbers\\n * line up whatever their width, and each row is a subgrid that paints its own tint. The grid\\n * carries `dir=\\\"ltr\\\"`: code reads left to right in a right-to-left page; everything around\\n * it uses logical properties and follows the page.\\n *\\n * The tints are the plugin's tokens, each falling back to core's: added on the success\\n * surface, removed on the error surface, so a theme that moves those moves the diff.\\n */\\naparte-diff {\\n display: block;\\n min-inline-size: 0;\\n color: var(--aparte-text);\\n font-family: var(--aparte-font-family);\\n font-size: var(--aparte-font-size-sm);\\n}\\n\\naparte-diff[hidden] {\\n display: none;\\n}\\n\\n.aparte-diff-file {\\n overflow: hidden;\\n border: var(--aparte-border-width) solid var(--aparte-code-border);\\n border-radius: var(--aparte-radius-md);\\n background: var(--aparte-code-bg);\\n}\\n\\n.aparte-diff-file + .aparte-diff-file {\\n margin-block-start: var(--aparte-space-3);\\n}\\n\\n/* ── The header: path, what happened, counts ─────────────────────────────── */\\n\\n.aparte-diff-file__head {\\n display: flex;\\n align-items: center;\\n gap: var(--aparte-space-2);\\n padding: var(--aparte-space-1) var(--aparte-space-2);\\n background: var(--aparte-code-header-bg);\\n color: var(--aparte-code-header-text);\\n}\\n\\n/* The path takes the room, and its button reads from the leading edge. */\\n.aparte-diff-file__toggle {\\n flex: 1 1 auto;\\n justify-content: flex-start;\\n min-inline-size: 0;\\n color: inherit;\\n}\\n\\n.aparte-diff-file__chevron {\\n display: inline-flex;\\n flex: none;\\n transition: transform var(--aparte-duration-fast);\\n}\\n\\n.aparte-diff-file__toggle[aria-expanded='false'] > .aparte-diff-file__chevron {\\n transform: rotate(-90deg);\\n}\\n\\n.aparte-diff-file__toggle[aria-expanded='false']:dir(rtl) > .aparte-diff-file__chevron {\\n transform: rotate(90deg);\\n}\\n\\n.aparte-diff-file__path {\\n overflow: hidden;\\n font-family: var(--aparte-code-font-family);\\n font-weight: var(--aparte-font-weight-semibold);\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n}\\n\\n.aparte-diff-file__status {\\n flex: none;\\n}\\n\\n.aparte-diff-file__stat {\\n display: inline-flex;\\n flex: none;\\n gap: var(--aparte-space-2);\\n padding-inline-end: var(--aparte-space-1);\\n font-family: var(--aparte-code-font-family);\\n font-variant-numeric: tabular-nums;\\n}\\n\\n.aparte-diff-file__added {\\n color: var(--aparte-diff-added-ink, var(--aparte-success-ink));\\n}\\n\\n.aparte-diff-file__removed {\\n color: var(--aparte-diff-removed-ink, var(--aparte-error-ink));\\n}\\n\\n/* ── The lines ───────────────────────────────────────────────────────────── */\\n\\n/* The rule under the header belongs to the lines: a folded file has none. */\\n.aparte-diff-file__lines {\\n display: grid;\\n grid-template-columns: max-content max-content max-content minmax(0, 1fr);\\n border-block-start: var(--aparte-border-width) solid var(--aparte-code-border);\\n font-family: var(--aparte-code-font-family);\\n font-size: var(--aparte-code-block-font-size);\\n line-height: var(--aparte-code-block-line-height);\\n}\\n\\n.aparte-diff-file__lines[hidden] {\\n display: none;\\n}\\n\\n.aparte-diff-line {\\n display: grid;\\n grid-column: 1 / -1;\\n grid-template-columns: subgrid;\\n}\\n\\n.aparte-diff-line[data-kind='add'] {\\n background: var(--aparte-diff-added-bg, var(--aparte-success-surface));\\n}\\n\\n.aparte-diff-line[data-kind='del'] {\\n background: var(--aparte-diff-removed-bg, var(--aparte-error-surface));\\n}\\n\\n/* Picked for a comment: its own tint, and a bar on the leading edge of the code. */\\n.aparte-diff-line[data-selected] {\\n background: var(--aparte-diff-selected-bg, var(--aparte-surface-hover));\\n box-shadow: inset var(--aparte-space-1) 0 0 var(--aparte-primary);\\n}\\n\\n.aparte-diff-line:focus {\\n outline: none;\\n}\\n\\n/* The two numbers. A `<span>`, or with `commentable` a button that draws as a number: no chrome. */\\n.aparte-diff-line__gutter {\\n display: grid;\\n grid-column: span 2;\\n grid-template-columns: subgrid;\\n margin: 0;\\n padding: 0;\\n border: 0;\\n background: none;\\n color: var(--aparte-text-muted);\\n font: inherit;\\n user-select: none;\\n}\\n\\nbutton.aparte-diff-line__gutter {\\n cursor: pointer;\\n}\\n\\nbutton.aparte-diff-line__gutter:hover,\\n.aparte-diff-line[data-selected] > .aparte-diff-line__gutter {\\n color: var(--aparte-text);\\n}\\n\\nbutton.aparte-diff-line__gutter:focus-visible {\\n outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);\\n outline-offset: calc(-1 * var(--aparte-focus-outline-width));\\n}\\n\\n.aparte-diff-line__num {\\n min-inline-size: 3ch;\\n padding-inline: var(--aparte-space-2);\\n font-variant-numeric: tabular-nums;\\n text-align: end;\\n}\\n\\n.aparte-diff-line__marker {\\n padding-inline: var(--aparte-space-1);\\n color: var(--aparte-text-muted);\\n user-select: none;\\n}\\n\\n.aparte-diff-line[data-kind='add'] > .aparte-diff-line__marker {\\n color: var(--aparte-diff-added-ink, var(--aparte-success-ink));\\n}\\n\\n.aparte-diff-line[data-kind='del'] > .aparte-diff-line__marker {\\n color: var(--aparte-diff-removed-ink, var(--aparte-error-ink));\\n}\\n\\n/* Wraps rather than scrolls: no line hides past the edge, and the view needs no tab stop to scroll. */\\n.aparte-diff-line__code {\\n padding-block: 0;\\n padding-inline: 0 var(--aparte-space-3);\\n border: 0;\\n background: none;\\n color: inherit;\\n font: inherit;\\n overflow-wrap: anywhere;\\n tab-size: 4;\\n white-space: pre-wrap;\\n}\\n\\n/* ── Between the lines ───────────────────────────────────────────────────── */\\n\\n.aparte-diff-fold,\\n.aparte-diff-eof,\\n.aparte-diff-file__lines > .aparte-diff-empty,\\n.aparte-diff-file__lines > .aparte-diff-binary {\\n grid-column: 1 / -1;\\n}\\n\\n.aparte-diff-fold {\\n display: flex;\\n align-items: center;\\n gap: var(--aparte-space-2);\\n min-inline-size: 0;\\n padding: var(--aparte-space-1) var(--aparte-space-2);\\n background: var(--aparte-surface-2);\\n color: var(--aparte-text-muted);\\n font-family: var(--aparte-font-family);\\n font-size: var(--aparte-font-size-sm);\\n}\\n\\n.aparte-diff-fold__show {\\n flex: none;\\n}\\n\\n.aparte-diff-fold__count {\\n flex: none;\\n padding-inline: var(--aparte-space-2);\\n}\\n\\n.aparte-diff-fold__section {\\n min-inline-size: 0;\\n overflow: hidden;\\n font-family: var(--aparte-code-font-family);\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n}\\n\\n.aparte-diff-eof {\\n padding-inline-start: var(--aparte-space-3);\\n color: var(--aparte-text-muted);\\n font-family: var(--aparte-font-family);\\n font-size: var(--aparte-font-size-xs);\\n}\\n\\n.aparte-diff-empty,\\n.aparte-diff-binary {\\n margin: 0;\\n padding: var(--aparte-space-3);\\n color: var(--aparte-text-muted);\\n font-family: var(--aparte-font-family);\\n}\\n\\n/* ── Comments ────────────────────────────────────────────────────────────── */\\n\\n.aparte-diff-comment,\\n.aparte-diff-form {\\n grid-column: 1 / -1;\\n margin-block: var(--aparte-space-2);\\n margin-inline: var(--aparte-space-3);\\n font-family: var(--aparte-font-family);\\n font-size: var(--aparte-font-size-sm);\\n line-height: var(--aparte-line-height-normal);\\n}\\n\\n.aparte-diff-comment {\\n padding: var(--aparte-space-2) var(--aparte-space-3);\\n border: var(--aparte-border-width) solid var(--aparte-border);\\n border-inline-start: var(--aparte-space-1) solid var(--aparte-primary);\\n border-radius: var(--aparte-radius-md);\\n background: var(--aparte-surface-1);\\n}\\n\\n.aparte-diff-comment__head {\\n display: flex;\\n align-items: center;\\n gap: var(--aparte-space-2);\\n color: var(--aparte-text-muted);\\n font-size: var(--aparte-font-size-xs);\\n}\\n\\n.aparte-diff-comment__text {\\n margin-block: var(--aparte-space-1) 0;\\n margin-inline: 0;\\n color: var(--aparte-text);\\n overflow-wrap: anywhere;\\n white-space: pre-wrap;\\n}\\n\\n.aparte-diff-form {\\n display: grid;\\n gap: var(--aparte-space-2);\\n}\\n\\n.aparte-diff-form__label {\\n color: var(--aparte-text-muted);\\n font-size: var(--aparte-font-size-xs);\\n}\\n\\n.aparte-diff-form__actions {\\n display: flex;\\n justify-content: flex-end;\\n gap: var(--aparte-space-2);\\n}\\n\\n@media (prefers-reduced-motion: reduce) {\\n .aparte-diff-file__chevron {\\n transition: none;\\n }\\n}\\n\"","/**\n * The plugin's one stylesheet, put on the page once, by the first `<aparte-diff>` to connect.\n */\nimport styles from './diff.css?raw';\n\nconst STYLE_ID = 'aparte-diff-styles';\n\n/** The sheet, as the element puts it on the page. */\nexport const DIFF_STYLES = styles;\n\n/** Put the sheet on the page, unless it is there already. */\nexport function ensureStyles(doc: Document = document): void {\n if (doc.getElementById(STYLE_ID)) return;\n const style = doc.createElement('style');\n style.id = STYLE_ID;\n style.textContent = styles;\n doc.head.appendChild(style);\n}\n","/**\n * The words `<aparte-diff>` says, in the chat's language.\n *\n * Core's locale is a closed list of what CORE draws, so this plugin reads its own keys off\n * `getLocale()` and defaults them here: the app's locale first, then this table for the\n * chat's language (the ten `@aparte/locales` ships), then English.\n *\n * aparteGlobalConfig.extendLocale({ diffCommentSubmit: 'Envoyer' });\n *\n * A count is a set of plural forms, each named by its `Intl.PluralRules` category, as core's\n * `turnTools` is: Russian needs `few` and `many`, Japanese, Korean and Chinese one form.\n */\nimport { pluralize, type AparteConfig } from '@aparte/core';\n\ntype Language = 'fr' | 'de' | 'es' | 'it' | 'ja' | 'ko' | 'pt' | 'ru' | 'zh';\ntype Word = { en: string } & Partial<Record<Language, string>>;\n\nexport const WORDS = {\n /** A file's added lines, said to a screen reader beside the `+3` it shows. */\n diffAdded: {\n en: 'one:{n} line added|other:{n} lines added',\n fr: 'one:{n} ligne ajoutée|other:{n} lignes ajoutées',\n de: 'one:{n} Zeile hinzugefügt|other:{n} Zeilen hinzugefügt',\n es: 'one:{n} línea añadida|other:{n} líneas añadidas',\n it: 'one:{n} riga aggiunta|other:{n} righe aggiunte',\n ja: 'other:{n} 行追加',\n ko: 'other:{n}줄 추가됨',\n pt: 'one:{n} linha adicionada|other:{n} linhas adicionadas',\n ru: 'one:{n} строка добавлена|few:{n} строки добавлены|many:{n} строк добавлено|other:{n} строки добавлено',\n zh: 'other:新增 {n} 行',\n },\n /** A file's removed lines, said to a screen reader beside the `−1` it shows. */\n diffRemoved: {\n en: 'one:{n} line removed|other:{n} lines removed',\n fr: 'one:{n} ligne supprimée|other:{n} lignes supprimées',\n de: 'one:{n} Zeile entfernt|other:{n} Zeilen entfernt',\n es: 'one:{n} línea eliminada|other:{n} líneas eliminadas',\n it: 'one:{n} riga rimossa|other:{n} righe rimosse',\n ja: 'other:{n} 行削除',\n ko: 'other:{n}줄 삭제됨',\n pt: 'one:{n} linha removida|other:{n} linhas removidas',\n ru: 'one:{n} строка удалена|few:{n} строки удалены|many:{n} строк удалено|other:{n} строки удалено',\n zh: 'other:删除 {n} 行',\n },\n /** What the `+` of an added line says to a screen reader. */\n diffMarkerAdded: {\n en: 'added', fr: 'ajoutée', de: 'hinzugefügt', es: 'añadida', it: 'aggiunta',\n ja: '追加', ko: '추가됨', pt: 'adicionada', ru: 'добавлена', zh: '新增',\n },\n /** What the `−` of a removed line says to a screen reader. */\n diffMarkerRemoved: {\n en: 'removed', fr: 'supprimée', de: 'entfernt', es: 'eliminada', it: 'rimossa',\n ja: '削除', ko: '삭제됨', pt: 'removida', ru: 'удалена', zh: '删除',\n },\n /** The button over a run of unchanged lines the element can show. */\n diffShowUnchanged: {\n en: 'one:Show {n} unchanged line|other:Show {n} unchanged lines',\n fr: 'one:Afficher {n} ligne inchangée|other:Afficher {n} lignes inchangées',\n de: 'one:{n} unveränderte Zeile anzeigen|other:{n} unveränderte Zeilen anzeigen',\n es: 'one:Mostrar {n} línea sin cambios|other:Mostrar {n} líneas sin cambios',\n it: 'one:Mostra {n} riga invariata|other:Mostra {n} righe invariate',\n ja: 'other:変更のない {n} 行を表示',\n ko: 'other:변경되지 않은 {n}줄 표시',\n pt: 'one:Mostrar {n} linha inalterada|other:Mostrar {n} linhas inalteradas',\n ru: 'one:Показать {n} неизменённую строку|few:Показать {n} неизменённые строки|many:Показать {n} неизменённых строк|other:Показать {n} неизменённой строки',\n zh: 'other:显示 {n} 行未更改的内容',\n },\n /** A run of unchanged lines the diff does not hold (a unified diff's text), between two hunks. */\n diffUnchanged: {\n en: 'one:{n} unchanged line|other:{n} unchanged lines',\n fr: 'one:{n} ligne inchangée|other:{n} lignes inchangées',\n de: 'one:{n} unveränderte Zeile|other:{n} unveränderte Zeilen',\n es: 'one:{n} línea sin cambios|other:{n} líneas sin cambios',\n it: 'one:{n} riga invariata|other:{n} righe invariate',\n ja: 'other:変更のない {n} 行',\n ko: 'other:변경되지 않은 {n}줄',\n pt: 'one:{n} linha inalterada|other:{n} linhas inalteradas',\n ru: 'one:{n} неизменённая строка|few:{n} неизменённые строки|many:{n} неизменённых строк|other:{n} неизменённой строки',\n zh: 'other:{n} 行未更改',\n },\n /** A new file's tag, beside its path. */\n diffStatusAdded: {\n en: 'Added', fr: 'Ajouté', de: 'Neu', es: 'Añadido', it: 'Aggiunto',\n ja: '追加', ko: '추가됨', pt: 'Adicionado', ru: 'Добавлен', zh: '新增',\n },\n /** A deleted file's tag, beside its path. */\n diffStatusDeleted: {\n en: 'Deleted', fr: 'Supprimé', de: 'Gelöscht', es: 'Eliminado', it: 'Eliminato',\n ja: '削除', ko: '삭제됨', pt: 'Excluído', ru: 'Удалён', zh: '已删除',\n },\n /** A renamed file's tag, beside its paths. */\n diffStatusRenamed: {\n en: 'Renamed', fr: 'Renommé', de: 'Umbenannt', es: 'Renombrado', it: 'Rinominato',\n ja: '名前変更', ko: '이름 변경됨', pt: 'Renomeado', ru: 'Переименован', zh: '已重命名',\n },\n /** Where a binary file's lines would be. */\n diffBinary: {\n en: 'Binary file, no lines to show', fr: 'Fichier binaire, aucune ligne à afficher',\n de: 'Binärdatei, keine Zeilen anzuzeigen', es: 'Archivo binario, no hay líneas que mostrar',\n it: 'File binario, nessuna riga da mostrare', ja: 'バイナリファイルのため表示する行はありません',\n ko: '바이너리 파일이라 표시할 줄이 없습니다', pt: 'Arquivo binário, nenhuma linha para mostrar',\n ru: 'Двоичный файл, строк для показа нет', zh: '二进制文件,没有可显示的行',\n },\n /** Under the last line of a file that ends without a newline. */\n diffNoNewline: {\n en: 'No newline at end of file', fr: 'Pas de saut de ligne à la fin du fichier',\n de: 'Kein Zeilenumbruch am Dateiende', es: 'Sin salto de línea al final del archivo',\n it: 'Nessun a capo alla fine del file', ja: 'ファイル末尾に改行がありません',\n ko: '파일 끝에 줄바꿈 없음', pt: 'Sem quebra de linha no fim do arquivo',\n ru: 'Нет перевода строки в конце файла', zh: '文件末尾没有换行符',\n },\n /** An unchanged line's number, as its button and a comment name it. */\n diffLine: {\n en: 'Line {n}', fr: 'Ligne {n}', de: 'Zeile {n}', es: 'Línea {n}', it: 'Riga {n}',\n ja: '{n} 行目', ko: '{n}번째 줄', pt: 'Linha {n}', ru: 'Строка {n}', zh: '第 {n} 行',\n },\n /** An added line's number, as its button names it. */\n diffLineAdded: {\n en: 'Added line {n}', fr: 'Ligne ajoutée {n}', de: 'Hinzugefügte Zeile {n}', es: 'Línea añadida {n}',\n it: 'Riga aggiunta {n}', ja: '追加された {n} 行目', ko: '추가된 {n}번째 줄', pt: 'Linha adicionada {n}',\n ru: 'Добавленная строка {n}', zh: '新增的第 {n} 行',\n },\n /** A removed line's number, as its button and a comment name it. */\n diffLineRemoved: {\n en: 'Removed line {n}', fr: 'Ligne supprimée {n}', de: 'Entfernte Zeile {n}', es: 'Línea eliminada {n}',\n it: 'Riga rimossa {n}', ja: '削除された {n} 行目', ko: '삭제된 {n}번째 줄', pt: 'Linha removida {n}',\n ru: 'Удалённая строка {n}', zh: '删除的第 {n} 行',\n },\n /** A range a comment is on. */\n diffLines: {\n en: 'Lines {start}–{end}', fr: 'Lignes {start} à {end}', de: 'Zeilen {start}–{end}', es: 'Líneas {start}–{end}',\n it: 'Righe {start}–{end}', ja: '{start}〜{end} 行目', ko: '{start}~{end}번째 줄', pt: 'Linhas {start}–{end}',\n ru: 'Строки {start}–{end}', zh: '第 {start}–{end} 行',\n },\n /** A range of removed lines a comment is on. */\n diffLinesRemoved: {\n en: 'Removed lines {start}–{end}', fr: 'Lignes supprimées {start} à {end}', de: 'Entfernte Zeilen {start}–{end}',\n es: 'Líneas eliminadas {start}–{end}', it: 'Righe rimosse {start}–{end}', ja: '削除された {start}〜{end} 行目',\n ko: '삭제된 {start}~{end}번째 줄', pt: 'Linhas removidas {start}–{end}', ru: 'Удалённые строки {start}–{end}',\n zh: '删除的第 {start}–{end} 行',\n },\n /** The comment field's name, on one line. */\n diffCommentLine: {\n en: 'Comment on line {n}', fr: 'Commenter la ligne {n}', de: 'Zeile {n} kommentieren', es: 'Comentar la línea {n}',\n it: 'Commenta la riga {n}', ja: '{n} 行目にコメント', ko: '{n}번째 줄에 댓글', pt: 'Comentar a linha {n}',\n ru: 'Комментарий к строке {n}', zh: '评论第 {n} 行',\n },\n /** The comment field's name, on a range. */\n diffCommentLines: {\n en: 'Comment on lines {start}–{end}', fr: 'Commenter les lignes {start} à {end}', de: 'Zeilen {start}–{end} kommentieren',\n es: 'Comentar las líneas {start}–{end}', it: 'Commenta le righe {start}–{end}', ja: '{start}〜{end} 行目にコメント',\n ko: '{start}~{end}번째 줄에 댓글', pt: 'Comentar as linhas {start}–{end}', ru: 'Комментарий к строкам {start}–{end}',\n zh: '评论第 {start}–{end} 行',\n },\n /** What the empty comment field shows. */\n diffCommentPlaceholder: {\n en: 'Leave a comment', fr: 'Laisser un commentaire', de: 'Kommentar schreiben', es: 'Deja un comentario',\n it: 'Lascia un commento', ja: 'コメントを入力', ko: '댓글 남기기', pt: 'Deixe um comentário',\n ru: 'Оставьте комментарий', zh: '留下评论',\n },\n /** The button that sends the comment. */\n diffCommentSubmit: {\n en: 'Comment', fr: 'Commenter', de: 'Kommentieren', es: 'Comentar', it: 'Commenta',\n ja: 'コメント', ko: '댓글 달기', pt: 'Comentar', ru: 'Комментировать', zh: '评论',\n },\n /** The button that closes the comment field. */\n diffCommentCancel: {\n en: 'Cancel', fr: 'Annuler', de: 'Abbrechen', es: 'Cancelar', it: 'Annulla',\n ja: 'キャンセル', ko: '취소', pt: 'Cancelar', ru: 'Отмена', zh: '取消',\n },\n /** A comment kept to be sent. */\n diffCommentPending: {\n en: 'Pending', fr: 'En attente', de: 'Ausstehend', es: 'Pendiente', it: 'In attesa',\n ja: '保留中', ko: '대기 중', pt: 'Pendente', ru: 'Ожидает', zh: '待发送',\n },\n /** A comment that was sent. */\n diffCommentSent: {\n en: 'Sent', fr: 'Envoyé', de: 'Gesendet', es: 'Enviado', it: 'Inviato',\n ja: '送信済み', ko: '보냄', pt: 'Enviado', ru: 'Отправлен', zh: '已发送',\n },\n /** A diff with no change in it. */\n diffEmpty: {\n en: 'No changes', fr: 'Aucun changement', de: 'Keine Änderungen', es: 'Sin cambios', it: 'Nessuna modifica',\n ja: '変更はありません', ko: '변경 사항 없음', pt: 'Nenhuma alteração', ru: 'Изменений нет', zh: '没有更改',\n },\n} satisfies Record<string, Word>;\n\nexport type WordKey = keyof typeof WORDS;\n\n/** The language part of the locale's tag: `pt-BR` → `pt`, `zh-CN` → `zh`, none → `en`. */\nfunction languageOf(config: AparteConfig): string {\n const tag = config.getLocale().tag;\n return typeof tag === 'string' && tag ? tag.split('-')[0]!.toLowerCase() : 'en';\n}\n\n/**\n * The word for `key`: the app's locale, else this table in the chat's language, else English;\n * `{name}` slots filled from `vars`, and a count's plural form chosen when `vars.n` is a number.\n */\nexport function word(config: AparteConfig, key: WordKey, vars: Record<string, string | number> = {}): string {\n const fromApp = (config.getLocale() as Record<string, unknown>)[key];\n const table: Word = WORDS[key];\n let text = (typeof fromApp === 'string' && fromApp) || table[languageOf(config) as Language] || table.en;\n if (typeof vars['n'] === 'number' && /^(zero|one|two|few|many|other):/.test(text)) {\n const tag = config.getLocale().tag;\n text = pluralize(text, vars['n'], typeof tag === 'string' && tag ? tag : undefined);\n }\n return text.replace(/\\{(\\w+)\\}/g, (slot, name: string) => (name in vars ? String(vars[name]) : slot));\n}\n","/**\n * `<aparte-diff>` — a change, as a reviewer reads it.\n *\n * Light DOM, like core: it writes its files as children, and a consumer's stylesheet reaches\n * every part. The data is the app's (`diff`, `comments`); what the element keeps is only what\n * the reader did to it — a file folded, a run of unchanged lines shown, a selection, a comment\n * being written — and the lines' colours once the highlighter gave them.\n */\nimport { resolveConfig, subscribeConfigChange, type AparteConfig } from '@aparte/core';\nimport { diffTexts, splitLines } from './compute.js';\nimport { highlightLines, languageOfPath } from './highlight.js';\nimport { parseUnifiedDiff } from './parse.js';\nimport { ensureStyles } from './styles.js';\nimport type {\n AparteDiffComment,\n AparteDiffCommentEventDetail,\n AparteDiffFile,\n AparteDiffInput,\n AparteDiffLine,\n AparteDiffSide,\n AparteDiffSource,\n} from './types.js';\nimport { word, type WordKey } from './words.js';\n\n/** Importable where there is no DOM: the class then extends nothing and is never defined. */\nconst ElementBase = (typeof HTMLElement === 'undefined' ? class {} : HTMLElement) as typeof HTMLElement;\n\n/** A run of unchanged lines between two hunks (or before the first, after the last). */\ninterface Gap {\n index: number;\n /** The old lines it holds, `from` to `to`. */\n from: number;\n to: number;\n /** What to add to an old line's number to get its new one. */\n delta: number;\n section?: string;\n}\n\n/** A file as the element shows it. */\ninterface Shown {\n file: AparteDiffFile;\n /** Every line of each side, when the texts were given: what a fold shows. */\n oldLines: string[] | null;\n newLines: string[] | null;\n oldEndsWithNewline: boolean;\n collapsed: boolean;\n expanded: Set<number>;\n /** Each side's lines as the highlighter coloured them, by line number, and the classes its block wore. */\n html: { old: Map<number, string>; new: Map<number, string>; className: string } | null;\n /** The colouring asked last: an answer to an earlier one is not drawn. */\n attempt: number;\n /** The line whose number takes the file's one tab stop. */\n roving: string | null;\n}\n\ntype Row = { type: 'line'; line: AparteDiffLine } | { type: 'gap'; gap: Gap };\n\n/** The selection: in which file, from which line to which (row keys). */\ninterface Selection {\n file: number;\n anchor: string;\n focus: string;\n}\n\n/** The comment field: one per element, moved under the selection. */\ninterface CommentForm {\n node: HTMLFormElement;\n input: HTMLTextAreaElement;\n label: HTMLLabelElement;\n submit: HTMLButtonElement;\n cancel: HTMLButtonElement;\n}\n\n/** A file's tag beside its path, by what happened to it: its word and its badge's intent. */\nconst STATUS_BADGES: Partial<Record<string, [WordKey, string]>> = {\n added: ['diffStatusAdded', 'success'],\n deleted: ['diffStatusDeleted', 'danger'],\n renamed: ['diffStatusRenamed', 'info'],\n};\n\nlet instances = 0;\n\n/** A side of `AparteDiffSource`: a text, or `null` for a file that is not there. */\nconst isSide = (side: unknown): side is string | null => typeof side === 'string' || side === null;\n\n/** A file's two texts, one of them `null` at most (a file created or deleted). */\nfunction isSource(value: unknown): value is AparteDiffSource {\n if (typeof value !== 'object' || value === null) return false;\n const { before, after } = value as { before?: unknown; after?: unknown };\n return isSide(before) && isSide(after) && (before !== null || after !== null);\n}\n\nfunction isFile(value: unknown): value is AparteDiffFile {\n return typeof value === 'object' && value !== null && Array.isArray((value as AparteDiffFile).hunks);\n}\n\n/** What the app gave, as files to show. */\nfunction filesOf(input: AparteDiffInput | null): Shown[] {\n const shown = (file: AparteDiffFile, source?: AparteDiffSource): Shown => {\n const old = source ? splitLines(source.before ?? '') : null;\n const lines = file.hunks.flatMap((h) => h.lines);\n return {\n file: {\n ...file,\n added: typeof file.added === 'number' ? file.added : lines.filter((l) => l.kind === 'add').length,\n removed: typeof file.removed === 'number' ? file.removed : lines.filter((l) => l.kind === 'del').length,\n },\n oldLines: old ? old.lines : null,\n newLines: source ? splitLines(source.after ?? '').lines : null,\n oldEndsWithNewline: old ? old.endsWithNewline : true,\n collapsed: false,\n expanded: new Set(),\n html: null,\n attempt: 0,\n roving: null,\n };\n };\n const one = (item: AparteDiffFile | AparteDiffSource): Shown[] => {\n if (isSource(item)) return [shown(diffTexts(item.before, item.after, { path: item.path, oldPath: item.oldPath }), item)];\n if (isFile(item)) return [shown(item)];\n return [];\n };\n if (input === null || input === undefined) return [];\n if (typeof input === 'string') return parseUnifiedDiff(input).map((file) => shown(file));\n if (Array.isArray(input)) return (input as ReadonlyArray<AparteDiffFile | AparteDiffSource>).flatMap(one);\n return one(input as AparteDiffSource);\n}\n\nconst firstOld = (h: { oldStart: number; oldCount: number }) => (h.oldCount > 0 ? h.oldStart : h.oldStart + 1);\nconst firstNew = (h: { newStart: number; newCount: number }) => (h.newCount > 0 ? h.newStart : h.newStart + 1);\n\n/** A row's key: a removed line by its old number, any other by its new one. */\nfunction keyOf(line: AparteDiffLine): string {\n return line.kind === 'del' ? `o${line.oldLine}` : `n${line.newLine}`;\n}\n\n/** The side a selection counts on: the old file's for a removed line, the new file's for the rest. */\nfunction sideOf(line: AparteDiffLine): AparteDiffSide {\n return line.kind === 'del' ? 'old' : 'new';\n}\n\nfunction numberOn(line: AparteDiffLine, side: AparteDiffSide): number | undefined {\n return side === 'old' ? line.oldLine : line.newLine;\n}\n\n/** The rows of a file: its hunks' lines, and the runs between them, folded or shown. */\nfunction rowsOf(shown: Shown): Row[] {\n const { file, oldLines } = shown;\n const rows: Row[] = [];\n const push = (gap: Gap) => {\n const count = gap.to - gap.from + 1;\n if (count <= 0 && !gap.section) return;\n if (oldLines && count > 0 && shown.expanded.has(gap.index)) {\n for (let k = gap.from; k <= gap.to; k++) {\n const line: AparteDiffLine = { kind: 'context', text: oldLines[k - 1] ?? '', oldLine: k, newLine: k + gap.delta };\n if (k === oldLines.length && !shown.oldEndsWithNewline) line.noNewline = true;\n rows.push({ type: 'line', line });\n }\n return;\n }\n rows.push({ type: 'gap', gap });\n };\n let nextOld = 1;\n let nextNew = 1;\n file.hunks.forEach((hunk, index) => {\n push({ index, from: nextOld, to: firstOld(hunk) - 1, delta: firstNew(hunk) - firstOld(hunk), section: hunk.section });\n for (const line of hunk.lines) rows.push({ type: 'line', line });\n nextOld = firstOld(hunk) + hunk.oldCount;\n nextNew = firstNew(hunk) + hunk.newCount;\n });\n // After the last hunk: only when the whole text is known, since nothing else says how long it is.\n if (oldLines && file.hunks.length) push({ index: file.hunks.length, from: nextOld, to: oldLines.length, delta: nextNew - nextOld });\n return rows;\n}\n\nfunction el<K extends keyof HTMLElementTagNameMap>(doc: Document, tag: K, className?: string, text?: string): HTMLElementTagNameMap[K] {\n const node = doc.createElement(tag);\n if (className) node.className = className;\n if (text !== undefined) node.textContent = text;\n return node;\n}\n\n/**\n * A change, as a reviewer reads it: each file under a header with its path and its counts\n * (`+3 −1`), each line with its old and new numbers and a `+` or `−` (said as a word to a\n * screen reader), the unchanged runs between hunks folded behind \"Show 24 unchanged lines\".\n * Set `diff` to a unified diff's text, to a file's two texts (`{ path, before, after }`: every\n * unchanged line can then be shown), or to a list of files.\n *\n * With `commentable`, a reader comments on a line or a range: a click on a line number (Shift+click\n * to stretch it), or from the keyboard — the numbers of a file are one tab stop, the arrows move,\n * Shift+arrows stretch, Enter or Space open the field, Escape lets go. Sending it fires\n * `aparte-diff-comment`; keeping the comments and sending them on is the app's, and the ones it\n * hands back through `comments` are shown under their lines with where each stands.\n *\n * The lines are coloured by the chat's highlighter when one is installed (`@aparte/plugin-shiki`),\n * in the language the path's extension names. The code reads left to right in a right-to-left page.\n *\n * @element aparte-diff\n *\n * @attr {boolean} commentable - Let the reader pick a line or a range and write a comment on it. Off by default: without it nothing is clickable.\n *\n * @fires {CustomEvent<AparteDiffCommentEventDetail>} aparte-diff-comment - A comment was written and sent: `{ path, side, startLine, endLine, text, code }`. Bubbles out of the chat; cancel it to keep the field open with its text.\n *\n * @cssprop [--aparte-diff-added-bg=var(--aparte-success-surface)] - The background of an added line.\n * @cssprop [--aparte-diff-removed-bg=var(--aparte-error-surface)] - The background of a removed line.\n * @cssprop [--aparte-diff-added-ink=var(--aparte-success-ink)] - The `+` of an added line, and the count of added lines.\n * @cssprop [--aparte-diff-removed-ink=var(--aparte-error-ink)] - The `−` of a removed line, and the count of removed lines.\n * @cssprop [--aparte-diff-selected-bg=var(--aparte-surface-hover)] - The background of the lines picked for a comment.\n *\n * @example A file's two texts, open to comments\n * ```html\n * <aparte-diff commentable></aparte-diff>\n * <script type=\"module\">\n * import '@aparte/plugin-diff';\n *\n * const diff = document.querySelector('aparte-diff');\n * diff.diff = { path: 'src/parse.ts', before: oldText, after: newText };\n * const comments = [];\n * diff.addEventListener('aparte-diff-comment', (event) => {\n * comments.push({ ...event.detail, state: 'pending' });\n * diff.comments = comments;\n * });\n * </script>\n * ```\n */\nexport class AparteDiff extends ElementBase {\n static get observedAttributes(): string[] { return ['commentable']; }\n\n private _input: AparteDiffInput | null = null;\n private _shown: Shown[] = [];\n /** Each file's element, as last drawn. */\n private _files: HTMLElement[] = [];\n private _comments: AparteDiffComment[] = [];\n private readonly _id = `aparte-diff-${++instances}`;\n private _generation = 0;\n private _stopConfig: (() => void) | null = null;\n private _selection: Selection | null = null;\n private _form: CommentForm | null = null;\n /** Whether the reader opened the field (it then follows the selection through a redraw). */\n private _writing = false;\n /** Each line row's line. */\n private readonly _lineOf = new WeakMap<Element, AparteDiffLine>();\n\n // ── What the app sets ──────────────────────────────────────────────────\n\n /** The change to show: a unified diff's text, `{ path, before, after }`, or a list of files (`AparteDiffFile` or texts). */\n get diff(): AparteDiffInput | null { return this._input; }\n set diff(value: AparteDiffInput | null) {\n this._input = value ?? null;\n this._shown = filesOf(this._input);\n this._generation++;\n this._selection = null;\n this._writing = false;\n this._form?.node.remove();\n if (this.isConnected) {\n this._render();\n void this._highlight();\n }\n }\n\n /** The comments to show under their lines, with their state. The element only shows them. */\n get comments(): AparteDiffComment[] { return this._comments; }\n set comments(value: AparteDiffComment[]) {\n this._comments = Array.isArray(value) ? [...value] : [];\n if (this.isConnected) this._render();\n }\n\n /** Whether a reader can comment on a line. Reflects the `commentable` attribute. */\n get commentable(): boolean { return this.hasAttribute('commentable'); }\n set commentable(value: boolean) { this.toggleAttribute('commentable', Boolean(value)); }\n\n // ── Lifecycle ──────────────────────────────────────────────────────────\n\n connectedCallback(): void {\n ensureStyles(this.ownerDocument);\n this.addEventListener('click', this._onClick);\n this.addEventListener('keydown', this._onKeydown);\n this.addEventListener('keyup', this._onKeyup);\n this._stopConfig?.();\n this._stopConfig = subscribeConfigChange(this, () => this._onConfigChange());\n this._render();\n void this._highlight();\n }\n\n disconnectedCallback(): void {\n this.removeEventListener('click', this._onClick);\n this.removeEventListener('keydown', this._onKeydown);\n this.removeEventListener('keyup', this._onKeyup);\n this._stopConfig?.();\n this._stopConfig = null;\n }\n\n attributeChangedCallback(name: string, previous: string | null, value: string | null): void {\n if (name !== 'commentable' || (previous === null) === (value === null)) return;\n if (value === null) {\n this._selection = null;\n this._writing = false;\n this._form?.node.remove();\n }\n if (this.isConnected) this._render();\n }\n\n // ── Drawing ────────────────────────────────────────────────────────────\n\n private get _config(): AparteConfig {\n return resolveConfig(this);\n }\n\n private _word(key: WordKey, vars?: Record<string, string | number>): string {\n return word(this._config, key, vars);\n }\n\n private _onConfigChange(): void {\n // A highlighter taken off leaves plain text, not the colours it gave.\n if (!this._config.hasHighlightProvider()) for (const shown of this._shown) shown.html = null;\n this._render();\n // Core says the config changed, not what changed in it: a highlighter installed, or\n // replaced, is one of those, so the lines are coloured again — one that failed before\n // gets another try. What they show stays until the new colours land.\n void this._highlight(true);\n }\n\n /** Draw every file again from the state, the reader's selection, field and focus kept. */\n private _render(): void {\n const doc = this.ownerDocument;\n const active = doc.activeElement;\n const focus = active && this.contains(active) ? this._focusKey(active) : null;\n // Nothing given yet draws nothing; a change with no file in it says so.\n this._files = this._shown.map((_, i) => this._renderFile(i));\n if (this._input === null) {\n this.replaceChildren();\n return;\n }\n if (!this._shown.length) {\n this.replaceChildren(this._message('aparte-diff-empty', 'diffEmpty'));\n return;\n }\n this.replaceChildren(...this._files);\n this._paintSelection();\n if (this._writing) this._placeForm(false);\n if (focus) this._restoreFocus(focus);\n }\n\n private _renderFile(index: number): HTMLElement {\n const doc = this.ownerDocument;\n const shown = this._shown[index]!;\n const { file } = shown;\n const status = file.status ?? (file.oldPath && file.oldPath !== file.path ? 'renamed' : 'modified');\n const root = el(doc, 'div', 'aparte-diff-file');\n root.dataset['path'] = file.path;\n root.dataset['file'] = String(index);\n\n const head = el(doc, 'div', 'aparte-diff-file__head');\n const toggle = el(doc, 'button', 'aparte-btn aparte-btn--ghost aparte-btn--sm aparte-diff-file__toggle');\n toggle.type = 'button';\n toggle.setAttribute('aria-expanded', String(!shown.collapsed));\n toggle.setAttribute('aria-controls', `${this._id}-f${index}`);\n const chevron = el(doc, 'span', 'aparte-diff-file__chevron');\n chevron.setAttribute('aria-hidden', 'true');\n chevron.innerHTML = this._config.getIcon('expand'); // safe-text: the icon provider's own markup\n const path = el(doc, 'span', 'aparte-diff-file__path', status === 'renamed' && file.oldPath ? `${file.oldPath} → ${file.path}` : file.path);\n toggle.append(chevron, path);\n head.append(toggle);\n const badge = STATUS_BADGES[status];\n if (badge) head.append(el(doc, 'span', `aparte-badge aparte-badge--sm aparte-badge--${badge[1]} aparte-diff-file__status`, this._word(badge[0])));\n if (!file.binary) {\n const stat = el(doc, 'span', 'aparte-diff-file__stat');\n const added = el(doc, 'span', 'aparte-diff-file__added', `+${file.added}`);\n const removed = el(doc, 'span', 'aparte-diff-file__removed', `−${file.removed}`);\n added.setAttribute('aria-hidden', 'true');\n removed.setAttribute('aria-hidden', 'true');\n const said = el(doc, 'span', 'aparte-sr-only', `${this._word('diffAdded', { n: file.added })}, ${this._word('diffRemoved', { n: file.removed })}`);\n stat.append(added, removed, said);\n head.append(stat);\n }\n\n const lines = el(doc, 'div', 'aparte-diff-file__lines');\n lines.id = `${this._id}-f${index}`;\n lines.setAttribute('dir', 'ltr');\n lines.hidden = shown.collapsed;\n if (file.binary) lines.append(this._message('aparte-diff-binary', 'diffBinary'));\n else if (!file.hunks.length) {\n if (status !== 'renamed') lines.append(this._message('aparte-diff-empty', 'diffEmpty'));\n } else this._fillLines(lines, index);\n root.append(head, lines);\n return root;\n }\n\n private _message(className: string, key: WordKey): HTMLElement {\n const p = el(this.ownerDocument, 'p', className, this._word(key));\n p.setAttribute('dir', 'auto');\n return p;\n }\n\n /** The rows of file `index`, its comments under their lines. */\n private _fillLines(lines: HTMLElement, index: number): void {\n const shown = this._shown[index]!;\n const commentable = this.commentable;\n const rows = rowsOf(shown);\n const lineRows: HTMLElement[] = [];\n for (const row of rows) {\n if (row.type === 'gap') {\n lines.append(this._gapRow(row.gap, index, Boolean(shown.oldLines)));\n continue;\n }\n const node = this._lineRow(row.line, index, commentable);\n lineRows.push(node);\n lines.append(node);\n if (row.line.noNewline) {\n const eof = el(this.ownerDocument, 'div', 'aparte-diff-eof', this._word('diffNoNewline'));\n eof.setAttribute('dir', 'auto');\n lines.append(eof);\n }\n }\n if (commentable && lineRows.length) {\n const roving = lineRows.find((r) => r.dataset['key'] === shown.roving) ?? lineRows[0]!;\n shown.roving = roving.dataset['key']!;\n (roving.querySelector('.aparte-diff-line__gutter') as HTMLElement).tabIndex = 0;\n }\n this._placeComments(lines, shown, lineRows);\n }\n\n private _gapRow(gap: Gap, file: number, expandable: boolean): HTMLElement {\n const doc = this.ownerDocument;\n const row = el(doc, 'div', 'aparte-diff-fold');\n row.setAttribute('dir', 'auto');\n const count = gap.to - gap.from + 1;\n if (count > 0) {\n if (expandable) {\n const show = el(doc, 'button', 'aparte-btn aparte-btn--ghost aparte-btn--sm aparte-diff-fold__show', this._word('diffShowUnchanged', { n: count }));\n show.type = 'button';\n show.dataset['file'] = String(file);\n show.dataset['gap'] = String(gap.index);\n row.append(show);\n } else row.append(el(doc, 'span', 'aparte-diff-fold__count', this._word('diffUnchanged', { n: count })));\n }\n if (gap.section) row.append(el(doc, 'span', 'aparte-diff-fold__section', gap.section));\n return row;\n }\n\n private _lineRow(line: AparteDiffLine, file: number, commentable: boolean): HTMLElement {\n const doc = this.ownerDocument;\n const shown = this._shown[file]!;\n const row = el(doc, 'div', 'aparte-diff-line');\n row.dataset['kind'] = line.kind;\n row.dataset['key'] = keyOf(line);\n this._lineOf.set(row, line);\n const gutter = commentable ? el(doc, 'button', 'aparte-diff-line__gutter') : el(doc, 'span', 'aparte-diff-line__gutter');\n gutter.append(\n this._number('old', line.oldLine),\n this._number('new', line.newLine),\n );\n const code = el(doc, 'code', 'aparte-diff-line__code');\n if (commentable) {\n const button = gutter as HTMLButtonElement;\n button.type = 'button';\n button.tabIndex = -1;\n const n = numberOn(line, sideOf(line))!;\n button.setAttribute('aria-label', this._word(line.kind === 'add' ? 'diffLineAdded' : line.kind === 'del' ? 'diffLineRemoved' : 'diffLine', { n }));\n button.setAttribute('aria-pressed', 'false');\n code.id = `${this._id}-${file}-${keyOf(line)}`;\n button.setAttribute('aria-describedby', code.id);\n }\n const marker = el(doc, 'span', 'aparte-diff-line__marker', line.kind === 'add' ? '+' : line.kind === 'del' ? '−' : ' ');\n marker.setAttribute('aria-hidden', 'true');\n row.append(gutter, marker);\n if (line.kind !== 'context') row.append(el(doc, 'span', 'aparte-sr-only', this._word(line.kind === 'add' ? 'diffMarkerAdded' : 'diffMarkerRemoved')));\n if (!this._colour(code, shown, line)) code.textContent = line.text;\n row.append(code);\n return row;\n }\n\n /** Put the highlighter's colours in a line's code, with its block's classes; `false` when it has none for it. */\n private _colour(code: Element, shown: Shown, line: AparteDiffLine): boolean {\n const html = this._htmlOf(shown, line);\n if (html === undefined) return false;\n code.innerHTML = html; // safe-text: the highlighter's output, through the config's sanitizer\n code.className = shown.html!.className ? `aparte-diff-line__code ${shown.html!.className}` : 'aparte-diff-line__code';\n return true;\n }\n\n private _number(side: AparteDiffSide, value: number | undefined): HTMLElement {\n const num = el(this.ownerDocument, 'span', 'aparte-diff-line__num', value === undefined ? '' : String(value));\n num.dataset['side'] = side;\n return num;\n }\n\n private _htmlOf(shown: Shown, line: AparteDiffLine): string | undefined {\n if (!shown.html) return undefined;\n if (line.kind === 'del') return shown.html.old.get(line.oldLine!);\n if (line.kind === 'add') return shown.html.new.get(line.newLine!);\n return shown.html.new.get(line.newLine!) ?? shown.html.old.get(line.oldLine!);\n }\n\n /** The comments the app handed back, each under the line it ends on, or at the end of its file when that line is folded. */\n private _placeComments(lines: HTMLElement, shown: Shown, rows: HTMLElement[]): void {\n const { file } = shown;\n const after = new Map<HTMLElement, Element>();\n for (const comment of this._comments) {\n const ours = comment.path === file.path || (comment.side === 'old' && comment.path === file.oldPath);\n if (!ours) continue;\n const row = rows.find((r) => {\n const line = this._lineOf.get(r)!;\n if (comment.side === 'old' ? line.kind === 'add' : line.kind === 'del') return false;\n return numberOn(line, comment.side) === comment.endLine;\n });\n const node = this._commentNode(comment);\n if (!row) {\n lines.append(node);\n continue;\n }\n (after.get(row) ?? row).after(node);\n after.set(row, node);\n }\n }\n\n private _commentNode(comment: AparteDiffComment): HTMLElement {\n const doc = this.ownerDocument;\n const node = el(doc, 'div', 'aparte-diff-comment');\n node.setAttribute('dir', 'auto');\n if (comment.state) node.dataset['state'] = comment.state;\n const head = el(doc, 'div', 'aparte-diff-comment__head');\n head.append(el(doc, 'span', 'aparte-diff-comment__lines', this._rangeWords(comment.side, comment.startLine, comment.endLine)));\n if (comment.state === 'pending' || comment.state === 'sent') {\n const pending = comment.state === 'pending';\n head.append(el(doc, 'span', `aparte-badge aparte-badge--sm aparte-badge--${pending ? 'warning' : 'success'} aparte-diff-comment__state`, this._word(pending ? 'diffCommentPending' : 'diffCommentSent')));\n }\n node.append(head, el(doc, 'p', 'aparte-diff-comment__text', comment.text));\n return node;\n }\n\n /** \"Line 12\", \"Lines 12–14\", \"Removed line 5\". */\n private _rangeWords(side: AparteDiffSide, start: number, end: number): string {\n if (start === end) return this._word(side === 'old' ? 'diffLineRemoved' : 'diffLine', { n: start });\n return this._word(side === 'old' ? 'diffLinesRemoved' : 'diffLines', { start, end });\n }\n\n // ── Colours ────────────────────────────────────────────────────────────\n\n /**\n * Colour each file not coloured yet — or, `again`, every file — each side as one text, and\n * put the colours in the lines on screen. A file is coloured only once its highlighter\n * answered: one that failed (still loading its grammars) leaves it plain, to be tried again.\n */\n private async _highlight(again = false): Promise<void> {\n const config = this._config;\n if (!config.hasHighlightProvider()) return;\n const generation = this._generation;\n await Promise.all(this._shown.map(async (shown, index) => {\n if (shown.html && !again) return;\n const attempt = ++shown.attempt;\n const language = languageOfPath(shown.file.path);\n const side = (which: AparteDiffSide): { lines: string[]; numbers: number[] } => {\n const full = which === 'old' ? shown.oldLines : shown.newLines;\n if (full) return { lines: full, numbers: full.map((_, i) => i + 1) };\n const own = shown.file.hunks.flatMap((h) => h.lines).filter((l) => l.kind === 'context' || l.kind === (which === 'old' ? 'del' : 'add'));\n return { lines: own.map((l) => l.text), numbers: own.map((l) => numberOn(l, which)!) };\n };\n const old = side('old');\n const next = side('new');\n const [oldHtml, newHtml] = await Promise.all([highlightLines(config, old.lines, language), highlightLines(config, next.lines, language)]);\n // A new change, or a later colouring of this file, has the last word.\n if (generation !== this._generation || attempt !== shown.attempt || (!oldHtml && !newHtml)) return;\n shown.html = {\n old: new Map((oldHtml?.lines ?? []).map((html, i) => [old.numbers[i]!, html])),\n new: new Map((newHtml?.lines ?? []).map((html, i) => [next.numbers[i]!, html])),\n className: newHtml?.className ?? oldHtml?.className ?? '',\n };\n this._paintColours(index);\n }));\n }\n\n private _paintColours(index: number): void {\n const shown = this._shown[index]!;\n const lines = this._linesOf(index);\n if (!lines) return;\n for (const row of lines.querySelectorAll('.aparte-diff-line')) {\n const line = this._lineOf.get(row);\n if (line) this._colour(row.querySelector('.aparte-diff-line__code')!, shown, line);\n }\n }\n\n private _linesOf(index: number): HTMLElement | null {\n return this._files[index]?.querySelector<HTMLElement>(':scope > .aparte-diff-file__lines') ?? null;\n }\n\n /** The line row of file `index` with the key `key`, if it is drawn. */\n private _rowOf(index: number, key: string): HTMLElement | undefined {\n const lines = this._linesOf(index);\n return lines ? [...lines.querySelectorAll<HTMLElement>(':scope > .aparte-diff-line')].find((row) => row.dataset['key'] === key) : undefined;\n }\n\n // ── What the reader does ───────────────────────────────────────────────\n\n private readonly _onClick = (event: MouseEvent): void => {\n const target = event.target as Element | null;\n if (!target || !this.contains(target)) return;\n const toggle = target.closest('.aparte-diff-file__toggle');\n if (toggle) {\n const index = Number(toggle.closest<HTMLElement>('.aparte-diff-file')!.dataset['file']);\n const shown = this._shown[index]!;\n shown.collapsed = !shown.collapsed;\n toggle.setAttribute('aria-expanded', String(!shown.collapsed));\n this._linesOf(index)!.hidden = shown.collapsed;\n return;\n }\n const show = target.closest<HTMLElement>('.aparte-diff-fold__show');\n if (show) {\n this._expand(Number(show.dataset['file']), Number(show.dataset['gap']));\n return;\n }\n const gutter = target.closest<HTMLElement>('button.aparte-diff-line__gutter');\n if (gutter && this.commentable) this._pick(gutter.closest<HTMLElement>('.aparte-diff-line')!, event.shiftKey);\n };\n\n private readonly _onKeydown = (event: KeyboardEvent): void => {\n const target = event.target as Element | null;\n if (!target || !this.commentable) return;\n if (this._form?.node.contains(target)) {\n if (event.key === 'Escape') {\n // The field's own Escape: a pane around the diff that closes on Escape stays open.\n event.preventDefault();\n event.stopPropagation();\n this._letGo(true);\n } else if (event.key === 'Enter' && (event.ctrlKey || event.metaKey) && target === this._form.input) {\n event.preventDefault();\n this._send();\n }\n return;\n }\n const gutter = target.closest<HTMLElement>('button.aparte-diff-line__gutter');\n if (!gutter || !this.contains(gutter)) return;\n const row = gutter.closest<HTMLElement>('.aparte-diff-line')!;\n const rows = this._rowsAround(row);\n const at = rows.indexOf(row);\n const moves: Record<string, number> = { ArrowDown: at + 1, ArrowUp: at - 1, Home: 0, End: rows.length - 1 };\n const move = moves[event.key];\n if (move !== undefined) {\n event.preventDefault();\n const next = rows[Math.max(0, Math.min(rows.length - 1, move))]!;\n if (event.shiftKey && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) this._stretch(row, next);\n this._focusRow(next);\n return;\n }\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n this._pick(row, event.shiftKey, true);\n return;\n }\n if (event.key === 'Escape' && this._selection) {\n event.preventDefault();\n this._letGo(true);\n }\n };\n\n /** A button acts on Space's keyup: the keydown already did. */\n private readonly _onKeyup = (event: KeyboardEvent): void => {\n if (event.key === ' ' && (event.target as Element | null)?.closest?.('button.aparte-diff-line__gutter')) event.preventDefault();\n };\n\n /** The line rows of the file `row` is in. */\n private _rowsAround(row: HTMLElement): HTMLElement[] {\n return [...row.closest('.aparte-diff-file__lines')!.querySelectorAll<HTMLElement>(':scope > .aparte-diff-line')];\n }\n\n private _fileOf(row: Element): number {\n return Number(row.closest<HTMLElement>('.aparte-diff-file')!.dataset['file']);\n }\n\n /** Move the file's tab stop to `row`'s number, and the focus with it. */\n private _focusRow(row: HTMLElement): void {\n const shown = this._shown[this._fileOf(row)]!;\n for (const other of this._rowsAround(row)) (other.querySelector('.aparte-diff-line__gutter') as HTMLElement).tabIndex = -1;\n const gutter = row.querySelector<HTMLElement>('.aparte-diff-line__gutter')!;\n gutter.tabIndex = 0;\n shown.roving = row.dataset['key']!;\n gutter.focus();\n }\n\n /** Shift+arrow: the selection runs from where it started (or from `from`) to `to`. */\n private _stretch(from: HTMLElement, to: HTMLElement): void {\n const file = this._fileOf(from);\n const selection = this._selection;\n if (!selection || selection.file !== file) this._selection = { file, anchor: from.dataset['key']!, focus: to.dataset['key']! };\n else selection.focus = to.dataset['key']!;\n this._paintSelection();\n if (this._writing) this._placeForm(false);\n }\n\n /** A click (or Enter, Space) on a line number: that line, or the selection stretched to it; then the field. */\n private _pick(row: HTMLElement, stretch: boolean, keyboard = false): void {\n const file = this._fileOf(row);\n const key = row.dataset['key']!;\n const selection = this._selection;\n if (stretch && selection && selection.file === file) selection.focus = key;\n else if (!(keyboard && selection && selection.file === file && this._selectedRows().includes(row))) {\n this._selection = { file, anchor: key, focus: key };\n }\n const shown = this._shown[file]!;\n shown.roving = key;\n for (const other of this._rowsAround(row)) (other.querySelector('.aparte-diff-line__gutter') as HTMLElement).tabIndex = other === row ? 0 : -1;\n this._paintSelection();\n this._writing = true;\n this._placeForm(true);\n }\n\n private _selectedRows(): HTMLElement[] {\n const selection = this._selection;\n const lines = selection && this._linesOf(selection.file);\n if (!selection || !lines) return [];\n const rows = [...lines.querySelectorAll<HTMLElement>(':scope > .aparte-diff-line')];\n const a = rows.findIndex((r) => r.dataset['key'] === selection.anchor);\n const b = rows.findIndex((r) => r.dataset['key'] === selection.focus);\n if (a < 0 || b < 0) return [];\n return rows.slice(Math.min(a, b), Math.max(a, b) + 1);\n }\n\n private _paintSelection(): void {\n const selected = new Set(this._selectedRows());\n for (const row of this.querySelectorAll<HTMLElement>('.aparte-diff-line')) {\n const on = selected.has(row);\n row.toggleAttribute('data-selected', on);\n const gutter = row.querySelector('button.aparte-diff-line__gutter');\n gutter?.setAttribute('aria-pressed', String(on));\n }\n }\n\n /** What the selection covers: its side, its first and last line there, and those lines' text. */\n private _range(): Omit<AparteDiffCommentEventDetail, 'text'> | null {\n const rows = this._selectedRows();\n const selection = this._selection;\n if (!rows.length || !selection) return null;\n const anchor = rows.find((r) => r.dataset['key'] === selection.anchor)!;\n const side = sideOf(this._lineOf.get(anchor)!);\n const lines = rows.map((r) => this._lineOf.get(r)!).filter((l) => numberOn(l, side) !== undefined && (side === 'old' ? l.kind !== 'add' : l.kind !== 'del'));\n const numbers = lines.map((l) => numberOn(l, side)!);\n return {\n path: side === 'old' ? (this._shown[selection.file]!.file.oldPath ?? this._shown[selection.file]!.file.path) : this._shown[selection.file]!.file.path,\n side,\n startLine: Math.min(...numbers),\n endLine: Math.max(...numbers),\n code: lines.map((l) => l.text).join('\\n'),\n };\n }\n\n /** The field, under the selection's last line, named for what it covers. */\n private _placeForm(focus: boolean): void {\n const rows = this._selectedRows();\n const range = this._range();\n if (!rows.length || !range) return;\n const form = this._form ?? this._buildForm();\n form.label.textContent = range.startLine === range.endLine\n ? this._word('diffCommentLine', { n: range.startLine })\n : this._word('diffCommentLines', { start: range.startLine, end: range.endLine });\n form.input.placeholder = this._word('diffCommentPlaceholder');\n form.submit.textContent = this._word('diffCommentSubmit');\n form.cancel.textContent = this._word('diffCommentCancel');\n form.submit.disabled = !form.input.value.trim();\n rows[rows.length - 1]!.after(form.node);\n if (focus) form.input.focus();\n }\n\n private _buildForm(): CommentForm {\n const doc = this.ownerDocument;\n const node = el(doc, 'form', 'aparte-diff-form');\n node.setAttribute('dir', 'auto');\n const label = el(doc, 'label', 'aparte-diff-form__label');\n const input = el(doc, 'textarea', 'aparte-field aparte-field--sm aparte-field--textarea aparte-diff-form__input');\n input.id = `${this._id}-comment`;\n input.rows = 3;\n input.setAttribute('dir', 'auto');\n label.htmlFor = input.id;\n const actions = el(doc, 'div', 'aparte-diff-form__actions');\n const cancel = el(doc, 'button', 'aparte-btn aparte-btn--ghost aparte-btn--sm aparte-diff-form__cancel');\n cancel.type = 'button';\n const submit = el(doc, 'button', 'aparte-btn aparte-btn--primary aparte-btn--solid aparte-btn--sm aparte-diff-form__submit');\n submit.type = 'submit';\n actions.append(cancel, submit);\n node.append(label, input, actions);\n input.addEventListener('input', () => {\n submit.disabled = !input.value.trim();\n });\n node.addEventListener('submit', (event) => {\n event.preventDefault();\n this._send();\n });\n cancel.addEventListener('click', () => this._letGo(true));\n this._form = { node, input, label, submit, cancel };\n return this._form;\n }\n\n private _send(): void {\n const form = this._form;\n const range = this._range();\n const text = form?.input.value.trim() ?? '';\n if (!form || !range || !text) return;\n const detail: AparteDiffCommentEventDetail = { ...range, text };\n const kept = this.dispatchEvent(new CustomEvent<AparteDiffCommentEventDetail>('aparte-diff-comment', {\n detail, bubbles: true, composed: true, cancelable: true,\n }));\n if (!kept) return;\n form.input.value = '';\n this._letGo(true);\n }\n\n /** Close the field and drop the selection; the focus goes back to the line it ended on. */\n private _letGo(returnFocus: boolean): void {\n const last = this._selectedRows().at(-1);\n this._selection = null;\n this._writing = false;\n this._form?.node.remove();\n this._paintSelection();\n if (returnFocus && last) this._focusRow(last);\n }\n\n /** Show the unchanged run `gap` of file `index`, and carry on from its first line. */\n private _expand(index: number, gap: number): void {\n const shown = this._shown[index]!;\n const before = rowsOf(shown).find((r): r is { type: 'gap'; gap: Gap } => r.type === 'gap' && r.gap.index === gap);\n shown.expanded.add(gap);\n const lines = this._linesOf(index);\n if (!lines) return;\n lines.replaceChildren();\n this._fillLines(lines, index);\n this._paintSelection();\n if (this._writing && this._selection?.file === index) this._placeForm(false);\n if (!before) return;\n const first = this._rowOf(index, `n${before.gap.from + before.gap.delta}`);\n if (!first) return;\n if (this.commentable) this._focusRow(first);\n else {\n first.tabIndex = -1;\n first.focus();\n }\n }\n\n // ── Focus across a redraw ──────────────────────────────────────────────\n\n private _focusKey(active: Element): string | null {\n if (this._form && active === this._form.input) return 'form';\n const file = active.closest<HTMLElement>('.aparte-diff-file')?.dataset['file'];\n if (file === undefined) return null;\n if (active.closest('.aparte-diff-file__toggle')) return `toggle:${file}`;\n const show = active.closest<HTMLElement>('.aparte-diff-fold__show');\n if (show) return `gap:${file}:${show.dataset['gap']}`;\n const row = active.closest<HTMLElement>('.aparte-diff-line');\n return row ? `line:${file}:${row.dataset['key']}` : null;\n }\n\n private _restoreFocus(key: string): void {\n if (key === 'form') {\n if (this._form?.node.isConnected) this._form.input.focus();\n return;\n }\n const [kind, file, rest] = key.split(':');\n const index = Number(file);\n const scope = this._files[index];\n const target = kind === 'toggle' ? scope?.querySelector<HTMLElement>('.aparte-diff-file__toggle')\n : kind === 'gap' ? [...scope?.querySelectorAll<HTMLElement>('.aparte-diff-fold__show') ?? []].find((b) => b.dataset['gap'] === rest)\n : this._rowOf(index, rest ?? '')?.querySelector<HTMLElement>('.aparte-diff-line__gutter');\n if (target instanceof HTMLButtonElement) target.focus();\n }\n}\n\nif (typeof customElements !== 'undefined' && !customElements.get('aparte-diff')) customElements.define('aparte-diff', AparteDiff);\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'aparte-diff': AparteDiff;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `@aparte/plugin-diff` — the DOM-free entry, for Node and SSR.
3
+ *
4
+ * Everything but the element: the computation (`diffTexts`, `parseUnifiedDiff`,
5
+ * `formatUnifiedDiff`), the setup and the types. A server can prepare a diff and send it as
6
+ * data or as text; `setupDiff()` registers the same display there, and draws nothing without
7
+ * a document. `scripts/check-node-import.mjs` asserts this entry imports without a DOM.
8
+ */
9
+ export { diffTexts } from './compute.js';
10
+ export type { DiffTextsOptions } from './compute.js';
11
+ export { parseUnifiedDiff, formatUnifiedDiff } from './parse.js';
12
+ export { setupDiff } from './setup.js';
13
+ export type { DiffSetupOptions } from './setup.js';
14
+ export type { AparteDiff } from './aparte-diff.js';
15
+ export type { AparteDiffComment, AparteDiffCommentEventDetail, AparteDiffFile, AparteDiffFileStatus, AparteDiffHunk, AparteDiffInput, AparteDiffLine, AparteDiffLineKind, AparteDiffSide, AparteDiffSource, } from './types.js';
16
+ //# sourceMappingURL=index.node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.node.d.ts","sourceRoot":"","sources":["../src/index.node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnD,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,YAAY,EACR,iBAAiB,EACjB,4BAA4B,EAC5B,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,gBAAgB,GACnB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { d, f, p, s } from "./setup-CQYdbRuR.js";
2
+ export {
3
+ d as diffTexts,
4
+ f as formatUnifiedDiff,
5
+ p as parseUnifiedDiff,
6
+ s as setupDiff
7
+ };
8
+ //# sourceMappingURL=index.node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.node.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `parseUnifiedDiff` and `formatUnifiedDiff`: the text a tool prints, read into files and
3
+ * written back. Pure string work, so both run on a server.
4
+ *
5
+ * The reader follows `git diff` and `diff -u`: a file starts at `diff --git` or at a `---`
6
+ * line followed by a `+++` one; its extended headers (`new file mode`, `deleted file mode`,
7
+ * `rename from` / `rename to`, `similarity index`, `Binary files … differ`) say what happened
8
+ * to it — a rename only when git says so, so `diff -u x.c.orig x.c` is a change to `x.c`; a hunk is
9
+ * read for exactly as many lines as its `@@` header counts, so a removed line that itself
10
+ * starts with `--` is a line, not the next file. What is around the diff — a commit message,
11
+ * an email's signature — is skipped.
12
+ */
13
+ import type { AparteDiffFile } from './types.js';
14
+ /**
15
+ * The files of a unified diff, in the order it lists them. A text that holds no diff gives
16
+ * an empty list. Windows line endings read the same as Unix ones.
17
+ *
18
+ * @example
19
+ * const files = parseUnifiedDiff(gitDiffOutput);
20
+ * files.map((f) => `${f.path} +${f.added} −${f.removed}`);
21
+ */
22
+ export declare function parseUnifiedDiff(text: string): AparteDiffFile[];
23
+ /**
24
+ * Files as a unified diff, the way `git diff` writes it: what `parseUnifiedDiff` reads back,
25
+ * and what `git apply` takes. With `diffTexts`, a server can send a patch as text. A file
26
+ * created or deleted carries git's `new file mode` / `deleted file mode` header (as a regular
27
+ * file, `100644`), which `git apply` needs to create one; a rename carries `rename from` and
28
+ * `rename to`.
29
+ *
30
+ * @example
31
+ * formatUnifiedDiff(diffTexts(before, after, { path: 'src/parse.ts' }));
32
+ * // diff --git a/src/parse.ts b/src/parse.ts
33
+ * // --- a/src/parse.ts
34
+ * // +++ b/src/parse.ts
35
+ * // @@ -12,7 +12,7 @@
36
+ * // …
37
+ */
38
+ export declare function formatUnifiedDiff(files: AparteDiffFile | readonly AparteDiffFile[]): string;
39
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,cAAc,EAAkC,MAAM,YAAY,CAAC;AAOjF;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE,CAyF/D;AA0HD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,cAAc,EAAE,GAAG,MAAM,CA0B3F"}