diff options
| author | Gabriel Schneider <[email protected]> | 2026-07-30 17:05:12 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-07-30 17:56:23 -0300 |
| commit | 8ec4e135e318c7799b2576a8729493c063da4a97 (patch) | |
| tree | ab37cdbb7f478eb996264cf941891f127f303c85 /vrsite/assets | |
| parent | cb05c363045bf0e7af5858f6d7bc26f026fa9d70 (diff) | |
| download | notevi-8ec4e135e318c7799b2576a8729493c063da4a97.tar.gz notevi-8ec4e135e318c7799b2576a8729493c063da4a97.zip | |
Diffstat (limited to 'vrsite/assets')
| -rw-r--r-- | vrsite/assets/app.js | 49 | ||||
| -rw-r--r-- | vrsite/assets/bash.scm | 21 | ||||
| -rw-r--r-- | vrsite/assets/c.scm | 36 | ||||
| -rw-r--r-- | vrsite/assets/cpp.scm | 25 | ||||
| -rw-r--r-- | vrsite/assets/go.scm | 34 | ||||
| -rw-r--r-- | vrsite/assets/javascript-params.scm | 12 | ||||
| -rw-r--r-- | vrsite/assets/javascript.scm | 52 | ||||
| -rw-r--r-- | vrsite/assets/json.scm | 13 | ||||
| -rw-r--r-- | vrsite/assets/jsx.scm | 16 | ||||
| -rw-r--r-- | vrsite/assets/python.scm | 50 | ||||
| -rw-r--r-- | vrsite/assets/style.css.tmpl | 102 | ||||
| -rw-r--r-- | vrsite/assets/typescript.scm | 15 |
12 files changed, 421 insertions, 4 deletions
diff --git a/vrsite/assets/app.js b/vrsite/assets/app.js index 79dc190..104cc85 100644 --- a/vrsite/assets/app.js +++ b/vrsite/assets/app.js @@ -138,6 +138,55 @@ for (const ta of document.querySelectorAll(".notform textarea")) if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) ta.form.requestSubmit(); }); +// ── conversation ────────────────────────────────────────────────────────── +// A session page opened from a note lands on that note, flashed the same way +// clicking a noted line flashes it. +const conv = document.querySelector(".conv[data-anchor]"); +if (conv && conv.dataset.anchor) { + const here = document.getElementById(conv.dataset.anchor); + if (here) { + here.scrollIntoView({block: "start"}); + here.classList.remove("flash"); void here.offsetWidth; here.classList.add("flash"); + } +} + +// The trip back: a note in a conversation opens the code it was written +// about. The card is the target, so aim anywhere on it — except at the links +// it already carries. +const goTo = w => { location.href = w.dataset.href; }; +for (const w of document.querySelectorAll(".notewrap[data-href]")) { + w.addEventListener("click", e => { if (!e.target.closest("a")) goTo(w); }); + w.addEventListener("keydown", e => { + if (e.key === "Enter" || e.key === " ") { e.preventDefault(); goTo(w); } + }); +} + +// ── arriving at a note ──────────────────────────────────────────────────── +// ?note=N means someone followed a note here rather than opening the file: +// show what they came for — notes focus (coverage hides the note), the card +// flashed in the pane, the lines it covers marked. +const want = new URLSearchParams(location.search).get("note"); +if (want && document.body.dataset.kind === "file" && /^\d+$/.test(want)) { + setMode("notes"); + const card = document.getElementById("note-" + want); + if (card) { + // the card's own range, not the lines' data-note: a line belongs to the + // first note that claimed it, so overlapping notes would mark nothing + const from = +card.dataset.start, to = +card.dataset.end || +card.dataset.start; + let first = null; + for (let n = from; n > 0 && n <= to; n++) { + const li = document.getElementById("L" + n); + if (!li) continue; + li.classList.add("focus"); + first = first || li; + } + // after the browser has had its own go at the #L anchor + if (first) requestAnimationFrame(() => first.scrollIntoView({block: "center"})); + card.scrollIntoView({block: "nearest"}); + card.classList.remove("flash"); void card.offsetWidth; card.classList.add("flash"); + } +} + // ── note filters ────────────────────────────────────────────────────────── // Live pages let the server filter (htmx); the static export filters the // rendered list in place, so the same form works either way. diff --git a/vrsite/assets/bash.scm b/vrsite/assets/bash.scm new file mode 100644 index 0000000..4c7926a --- /dev/null +++ b/vrsite/assets/bash.scm @@ -0,0 +1,21 @@ +; Bash highlights, from tree-sitter/tree-sitter-bash v0.25.1 +; queries/highlights.scm (MIT). Reordered so expansions stay visible inside the +; strings that contain them, and capture names mapped onto zed syntax keys. +; Upstream's @embedded captures are dropped: zed paints `embedded` as plain +; foreground, so they only cost query time. + +["$" "&&" "||" ">" ">>" "<" "|" ";" "=" "!"] @operator + +["case" "do" "done" "elif" "else" "esac" "export" "fi" "for" "function" "if" + "in" "local" "readonly" "select" "then" "unset" "until" "while"] @keyword + +[(string) (raw_string) (heredoc_body) (heredoc_start)] @string +(variable_name) @property +(file_descriptor) @number + +(command_name) @function +(function_definition name: (word) @function) +((command (_) @constant) + (#match? @constant "^-")) + +(comment) @comment diff --git a/vrsite/assets/c.scm b/vrsite/assets/c.scm new file mode 100644 index 0000000..789bf93 --- /dev/null +++ b/vrsite/assets/c.scm @@ -0,0 +1,36 @@ +; C highlights, from tree-sitter/tree-sitter-c v0.24.2 queries/highlights.scm +; (MIT). Reordered for later-patterns-win, capture names mapped onto zed syntax +; keys (preprocessor directives get @preproc rather than upstream's @keyword, +; which zed themes colour separately). Also loaded ahead of cpp.scm, the way +; upstream composes the C++ highlights. + +["(" ")" "[" "]" "{" "}"] @punctuation.bracket +[";" "," "."] @punctuation.delimiter +["--" "-" "-=" "->" "=" "!=" "*" "&" "&&" "+" "++" "+=" "<" "==" ">" + "||"] @operator + +["break" "case" "const" "continue" "default" "do" "else" "enum" "extern" + "for" "if" "inline" "return" "sizeof" "static" "struct" "switch" "typedef" + "union" "volatile" "while"] @keyword + +["#define" "#elif" "#else" "#endif" "#if" "#ifdef" "#ifndef" + "#include"] @preproc +(preproc_directive) @preproc + +[(type_identifier) (primitive_type) (sized_type_specifier)] @type +(field_identifier) @property +(statement_identifier) @label +((identifier) @constant + (#match? @constant "^[A-Z][A-Z0-9_]*$")) + +(call_expression function: (identifier) @function) +(call_expression function: (field_expression + field: (field_identifier) @function.method)) +(function_declarator declarator: (identifier) @function) +(preproc_function_def name: (identifier) @function) + +(number_literal) @number +(null) @constant +[(string_literal) (char_literal) (system_lib_string)] @string +(escape_sequence) @string.escape +(comment) @comment diff --git a/vrsite/assets/cpp.scm b/vrsite/assets/cpp.scm new file mode 100644 index 0000000..e939a7a --- /dev/null +++ b/vrsite/assets/cpp.scm @@ -0,0 +1,25 @@ +; C++ highlights, from tree-sitter/tree-sitter-cpp v0.23.4 +; queries/highlights.scm (MIT). A supplement loaded after c.scm, as upstream +; composes it, so these patterns win. Capture names mapped onto zed syntax +; keys; `this` gets @variable.special to match what rust.scm does for `self`. + +["catch" "class" "co_await" "co_return" "co_yield" "constexpr" "constinit" + "consteval" "delete" "explicit" "final" "friend" "mutable" "namespace" + "noexcept" "new" "override" "private" "protected" "public" "template" + "throw" "try" "typename" "using" "concept" "requires" "virtual"] @keyword + +(auto) @type +((namespace_identifier) @type + (#match? @type "^[A-Z]")) + +(call_expression function: (qualified_identifier + name: (identifier) @function)) +(template_function name: (identifier) @function) +(template_method name: (field_identifier) @function.method) +(function_declarator declarator: (qualified_identifier + name: (identifier) @function)) +(function_declarator declarator: (field_identifier) @function.method) + +(raw_string_literal) @string +(this) @variable.special +(null "nullptr" @constant) diff --git a/vrsite/assets/go.scm b/vrsite/assets/go.scm new file mode 100644 index 0000000..56f676c --- /dev/null +++ b/vrsite/assets/go.scm @@ -0,0 +1,34 @@ +; Go highlights, from tree-sitter/tree-sitter-go v0.25.0 queries/highlights.scm +; (MIT). Reordered — upstream is written first-pattern-wins, this engine paints +; later patterns over earlier ones — capture names mapped onto zed syntax keys, +; and the builtin-function #match? turned into #any-of?, which needs no regex. + +["(" ")" "[" "]" "{" "}"] @punctuation.bracket +[";" "," "." ":"] @punctuation.delimiter +["--" "-" "-=" ":=" "!" "!=" "..." "*" "*=" "/" "/=" "&" "&&" "&=" "%" "%=" + "^" "^=" "+" "++" "+=" "<-" "<" "<<" "<<=" "<=" "=" "==" ">" ">=" ">>" ">>=" + "|" "|=" "||" "~"] @operator + +["break" "case" "chan" "const" "continue" "default" "defer" "else" + "fallthrough" "for" "func" "go" "goto" "if" "import" "interface" "map" + "package" "range" "return" "select" "struct" "switch" "type" "var"] @keyword + +(type_identifier) @type +(field_identifier) @property +(label_name) @label + +(function_declaration name: (identifier) @function) +(method_declaration name: (field_identifier) @function.method) +(call_expression function: (identifier) @function) +(call_expression function: (selector_expression + field: (field_identifier) @function.method)) +((call_expression function: (identifier) @function.builtin) + (#any-of? @function.builtin "append" "cap" "clear" "close" "complex" "copy" + "delete" "imag" "len" "make" "max" "min" "new" "panic" "print" "println" + "real" "recover")) + +[(int_literal) (float_literal) (imaginary_literal)] @number +[(true) (false) (nil) (iota)] @constant +[(interpreted_string_literal) (raw_string_literal) (rune_literal)] @string +(escape_sequence) @string.escape +(comment) @comment diff --git a/vrsite/assets/javascript-params.scm b/vrsite/assets/javascript-params.scm new file mode 100644 index 0000000..db300e3 --- /dev/null +++ b/vrsite/assets/javascript-params.scm @@ -0,0 +1,12 @@ +; JavaScript parameters, from tree-sitter/tree-sitter-javascript v0.25.0 +; queries/highlights-params.scm (MIT), verbatim. Its own file because upstream +; keeps it out of the TypeScript composition: there a formal_parameters child +; is always a required_parameter or optional_parameter, so these patterns are +; not merely dead but rejected by the query compiler as impossible. + +(formal_parameters [ + (identifier) @variable.parameter + (array_pattern (identifier) @variable.parameter) + (object_pattern [ + (pair_pattern value: (identifier) @variable.parameter) + (shorthand_property_identifier_pattern) @variable.parameter])]) diff --git a/vrsite/assets/javascript.scm b/vrsite/assets/javascript.scm new file mode 100644 index 0000000..a5c8f01 --- /dev/null +++ b/vrsite/assets/javascript.scm @@ -0,0 +1,52 @@ +; JavaScript highlights, from tree-sitter/tree-sitter-javascript v0.25.0 +; queries/highlights.scm (MIT). Reordered for later-patterns-win and capture +; names mapped onto zed syntax keys. Upstream's @variable.builtin and require() +; patterns are gone: they lean on (#is-not? local), which no query engine +; outside nvim evaluates. Also loaded ahead of typescript.scm, the way upstream +; composes the TypeScript highlights. + +["(" ")" "[" "]" "{" "}"] @punctuation.bracket +[";" (optional_chain) "." ","] @punctuation.delimiter +["-" "--" "-=" "+" "++" "+=" "*" "*=" "**" "**=" "/" "/=" "%" "%=" "<" "<=" + "<<" "<<=" "=" "==" "===" "!" "!=" "!==" "=>" ">" ">=" ">>" ">>=" ">>>" + ">>>=" "~" "^" "&" "|" "^=" "&=" "|=" "&&" "||" "??" "&&=" "||=" + "??="] @operator + +["as" "async" "await" "break" "case" "catch" "class" "const" "continue" + "debugger" "default" "delete" "do" "else" "export" "extends" "finally" "for" + "from" "function" "get" "if" "import" "in" "instanceof" "let" "new" "of" + "return" "set" "static" "switch" "target" "throw" "try" "typeof" "var" + "void" "while" "with" "yield"] @keyword + +[(property_identifier) (shorthand_property_identifier)] @property + +((identifier) @constructor + (#match? @constructor "^[A-Z]")) +([(identifier) (shorthand_property_identifier) + (shorthand_property_identifier_pattern)] @constant + (#match? @constant "^[A-Z_][A-Z0-9_]+$")) + +(function_expression name: (identifier) @function) +(function_declaration name: (identifier) @function) +(method_definition name: (property_identifier) @function.method) +(pair key: (property_identifier) @function.method + value: [(function_expression) (arrow_function)]) +(assignment_expression + left: (member_expression property: (property_identifier) @function.method) + right: [(function_expression) (arrow_function)]) +(variable_declarator name: (identifier) @function + value: [(function_expression) (arrow_function)]) +(assignment_expression left: (identifier) @function + right: [(function_expression) (arrow_function)]) +(call_expression function: (identifier) @function) +(call_expression function: (member_expression + property: (property_identifier) @function.method)) + +[(this) (super)] @variable.special +(number) @number +[(true) (false) (null) (undefined)] @constant +[(string) (template_string)] @string +(escape_sequence) @string.escape +(regex) @string.regex +(template_substitution "${" @punctuation.special "}" @punctuation.special) +(comment) @comment diff --git a/vrsite/assets/json.scm b/vrsite/assets/json.scm new file mode 100644 index 0000000..e82e125 --- /dev/null +++ b/vrsite/assets/json.scm @@ -0,0 +1,13 @@ +; JSON highlights, from tree-sitter/tree-sitter-json v0.24.8 +; queries/highlights.scm (MIT). Reordered so object keys repaint the string +; they are; capture names are already zed syntax keys but for @escape. + +["{" "}" "[" "]"] @punctuation.bracket +[":" ","] @punctuation.delimiter + +(number) @number +[(null) (true) (false)] @constant +(string) @string +(pair key: (string) @string.special.key) +(escape_sequence) @string.escape +(comment) @comment diff --git a/vrsite/assets/jsx.scm b/vrsite/assets/jsx.scm new file mode 100644 index 0000000..708eba2 --- /dev/null +++ b/vrsite/assets/jsx.scm @@ -0,0 +1,16 @@ +; JSX highlights, from tree-sitter/tree-sitter-javascript v0.25.0 +; queries/highlights-jsx.scm (MIT), verbatim but for the capture rename. +; Loaded last for .jsx and .tsx; the plain typescript grammar has no JSX nodes, +; so .ts must not get this file. + +(jsx_opening_element ["<" ">"] @punctuation.bracket) +(jsx_closing_element ["</" ">"] @punctuation.bracket) +(jsx_self_closing_element ["<" "/>"] @punctuation.bracket) + +(jsx_attribute (property_identifier) @attribute) + +; lowercase names are html elements, capitalized ones are components and keep +; the @constructor javascript.scm gave them +((jsx_opening_element (identifier) @tag) (#match? @tag "^[a-z][^.]*$")) +((jsx_closing_element (identifier) @tag) (#match? @tag "^[a-z][^.]*$")) +((jsx_self_closing_element (identifier) @tag) (#match? @tag "^[a-z][^.]*$")) diff --git a/vrsite/assets/python.scm b/vrsite/assets/python.scm new file mode 100644 index 0000000..f64e673 --- /dev/null +++ b/vrsite/assets/python.scm @@ -0,0 +1,50 @@ +; Python highlights, from tree-sitter/tree-sitter-python v0.25.0 +; queries/highlights.scm (MIT). Reordered for later-patterns-win, capture names +; mapped onto zed syntax keys, and the builtin-function #match? turned into +; #any-of?. The two casing heuristics stay regexes: nothing else can express +; "CONST_CASE" and "ClassName" over bare identifiers. + +["(" ")" "[" "]" "{" "}"] @punctuation.bracket +[";" "," "." ":"] @punctuation.delimiter +["-" "-=" "!=" "*" "**" "**=" "*=" "/" "//" "//=" "/=" "&" "&=" "%" "%=" + "^" "^=" "+" "->" "+=" "<" "<<" "<<=" "<=" "<>" "=" ":=" "==" ">" ">=" + ">>" ">>=" "|" "|=" "~" "@=" "and" "in" "is" "not" "or" "is not" + "not in"] @operator + +["as" "assert" "async" "await" "break" "class" "continue" "def" "del" "elif" + "else" "except" "exec" "finally" "for" "from" "global" "if" "import" "lambda" + "nonlocal" "pass" "print" "raise" "return" "try" "while" "with" "yield" + "match" "case"] @keyword + +((identifier) @constructor + (#match? @constructor "^[A-Z]")) +((identifier) @constant + (#match? @constant "^[A-Z][A-Z_0-9]*$")) +((identifier) @variable.special + (#any-of? @variable.special "self" "cls")) + +(type (identifier) @type) +(attribute attribute: (identifier) @property) +(function_definition name: (identifier) @function) +(call function: (identifier) @function) +(call function: (attribute attribute: (identifier) @function.method)) +((call function: (identifier) @function.builtin) + (#any-of? @function.builtin "abs" "all" "any" "ascii" "bin" "bool" + "breakpoint" "bytearray" "bytes" "callable" "chr" "classmethod" "compile" + "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" "exec" "filter" + "float" "format" "frozenset" "getattr" "globals" "hasattr" "hash" "help" + "hex" "id" "input" "int" "isinstance" "issubclass" "iter" "len" "list" + "locals" "map" "max" "memoryview" "min" "next" "object" "oct" "open" "ord" + "pow" "print" "property" "range" "repr" "reversed" "round" "set" "setattr" + "slice" "sorted" "staticmethod" "str" "sum" "super" "tuple" "type" "vars" + "zip" "__import__")) + +[(integer) (float)] @number +[(none) (true) (false)] @constant +(string) @string +(escape_sequence) @string.escape +(interpolation "{" @punctuation.special "}" @punctuation.special) + +(decorator "@" @attribute) +(decorator [(identifier) (attribute)] @attribute) +(comment) @comment diff --git a/vrsite/assets/style.css.tmpl b/vrsite/assets/style.css.tmpl index a808203..f57e454 100644 --- a/vrsite/assets/style.css.tmpl +++ b/vrsite/assets/style.css.tmpl @@ -73,7 +73,44 @@ nav > header { } nav > header a { color: var(--fg); } nav .brand { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -nav .atrev { margin: 0 0 8px; } + +/* ── the current change ──────────────────────────────────────── + Everything on screen is read at one change: the tree, the code, the + coverage, the notes, and anything you write. It says so, always. */ +.changebar { + margin-bottom: 10px; padding: 7px 8px; + border: 1px solid var(--border); border-left: 3px solid var(--accent); + border-radius: 6px; background: var(--bg); +} +.changebar.away { border-left-color: var(--note); } +.changebar .cid { display: flex; align-items: baseline; gap: 6px; } +.changebar .lbl { color: var(--muted); font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; } +.changebar .cid code { font-size: 13px; font-weight: 600; color: var(--fg); } +.changebar .cid a code { color: var(--accent); } +.changebar .flags { color: var(--note); font-size: 10px; font-style: italic; margin-left: auto; } +.changebar .desc { + margin: 3px 0 0; font-size: 11.5px; color: var(--muted); + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} +.changebar .counts { margin: 5px 0 0; font-size: 11px; color: var(--muted); } +.changebar .counts b { color: var(--fg); font-weight: 600; } +.changebar .away { color: var(--note); } +.changebar .scope { display: flex; gap: 0; margin-top: 7px; border: 1px solid var(--border); border-radius: 5px; overflow: hidden; } +.changebar .scope a { + flex: 1; text-align: center; padding: 2px 4px; + font-size: 11px; color: var(--muted); text-decoration: none; +} +.changebar .scope a:hover { background: var(--hover); color: var(--fg); } +.changebar .scope a.on { background: var(--hover); color: var(--fg); font-weight: 600; } +.changebar .revjump { margin-top: 6px; } +.changebar .revjump input { width: 100%; font-family: ui-monospace, Menlo, monospace; font-size: 11px; } +.changebar .back { margin: 6px 0 0; font-size: 11px; } + +/* a note written at another change: it pins to line numbers that have moved */ +.note.away { border-style: dashed; opacity: 0.82; } +.note.away:hover { opacity: 1; } +.atchg.away { color: var(--note); } +.atchg.away code { color: inherit; } .find, .filters input, .filters select, .notform input, .notform textarea, .jjform input { background: var(--bg); color: var(--fg); border: 1px solid var(--border); border-radius: 5px; @@ -183,6 +220,8 @@ ol.code li { white-space: pre; min-height: 1lh; padding-right: 16px; + /* a #L42 jump must clear the sticky header, or the line lands under it */ + scroll-margin-top: 76px; } ol.code li::before { content: counter(ln); @@ -222,8 +261,62 @@ aside summary { cursor: pointer; color: var(--muted); } .note footer { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; font-size: 11px; } .note footer code { color: var(--muted); } .note .model { opacity: 0.85; } +.note .conv { margin-left: auto; } .notecols { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 0 14px; } +/* ── conversation ──────────────────────────────────────────── */ +.convpage { padding: 0 16px 40vh; max-width: 1100px; } +.turn { + display: grid; grid-template-columns: 12ch 8ch minmax(0, 1fr); + gap: 0 10px; align-items: baseline; + padding: 2px 0 2px 8px; border-left: 2px solid transparent; +} +.turn .role { + color: var(--muted); font-size: 11px; text-align: right; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} +.turn time { color: var(--ln); font-size: 11px; font-variant-numeric: tabular-nums; } +.turn p { margin: 0; line-height: 1.5; white-space: pre-wrap; } +.turn .what { + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} +.t-say { padding: 8px 0 8px 8px; } +.t-user { border-left-color: var(--accent); } +.t-user .role, .t-user p { color: var(--fg); } +.t-assistant .role { color: var(--cov); } +.t-thinking p { color: var(--muted); font-style: italic; } +.t-tool .role { color: var(--note); } +.t-result .what { color: var(--muted); } +.t-result.bad .what { color: var(--note); } +.t-vr { border-left-color: var(--cov); background: color-mix(in srgb, var(--cov) 7%, transparent); } +.t-vr .role { color: var(--cov); font-weight: 600; } +/* A note is what you came to the conversation to find, so it breaks the run + of log lines: the note color's rail, a tinted band, a card instead of a + line. scroll-margin clears the sticky header — the anchored one is + scrolled to on load. */ +.t-note { + padding: 8px 0 8px 8px; + border-left-color: var(--note); + background: color-mix(in srgb, var(--note) 7%, transparent); +} +.t-note .role { color: var(--note); font-weight: 600; } +.t-note .role::before { content: "◆ "; font-size: 9px; vertical-align: 1px; } +.t-note .note { max-width: 640px; margin: 0; scroll-margin-top: 96px; } +.t-note.here { background: color-mix(in srgb, var(--accent) 12%, transparent); } +.t-note.here .note { border-color: var(--accent); border-left-color: var(--accent); } +/* the whole card is the way back to the code; the links inside still work */ +.notewrap { cursor: pointer; } +.notewrap:hover .note, .notewrap:focus-visible .note { + border-color: var(--accent); background: var(--hover); +} +.notewrap:focus-visible { outline: none; } +.notewrap[data-href] .note::after { + content: "open in source →"; display: block; + margin-top: 6px; color: var(--accent); font-size: 11px; opacity: 0; +} +.notewrap:hover .note::after, .notewrap:focus-visible .note::after { opacity: 1; } + /* ── live activity ─────────────────────────────────────────── */ .activity { margin-bottom: 10px; } .actlist { display: flex; flex-direction: column; gap: 3px; } @@ -279,9 +372,10 @@ aside summary { cursor: pointer; color: var(--muted); } .asidehead { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; } aside .meta { font-size: 11px; } -/* selection while picking lines to comment on */ -ol.code li.sel { background: color-mix(in srgb, var(--accent) 16%, transparent); } -ol.code li.sel::before { color: var(--accent); } +/* lines picked to comment on, and lines you arrived at from a note */ +ol.code li.sel, ol.code li.focus { background: color-mix(in srgb, var(--accent) 16%, transparent); } +ol.code li.sel::before, ol.code li.focus::before { color: var(--accent); } +ol.code li.focus { box-shadow: inset 3px 0 var(--accent); } /* ── jj ────────────────────────────────────────────────────── */ .jjform { display: flex; gap: 6px; align-items: center; padding: 10px 0; } diff --git a/vrsite/assets/typescript.scm b/vrsite/assets/typescript.scm new file mode 100644 index 0000000..96cf9c3 --- /dev/null +++ b/vrsite/assets/typescript.scm @@ -0,0 +1,15 @@ +; TypeScript highlights, from tree-sitter/tree-sitter-typescript v0.23.2 +; queries/highlights.scm (MIT). A supplement loaded after javascript.scm, as +; upstream composes it, so these patterns win. Upstream's +; ((identifier) @type (#match? "^[A-Z]")) is dropped: javascript.scm already +; paints capitalized identifiers as @constructor, and repainting every one of +; them as a type costs a second regex per identifier for a worse answer. + +["abstract" "declare" "enum" "implements" "interface" "keyof" "namespace" + "private" "protected" "public" "type" "readonly" "override" + "satisfies"] @keyword + +[(type_identifier) (predefined_type)] @type +(type_arguments "<" @punctuation.bracket ">" @punctuation.bracket) +(required_parameter (identifier) @variable.parameter) +(optional_parameter (identifier) @variable.parameter) |
