feat: cv2

This commit is contained in:
2026-01-09 14:42:33 +01:00
parent 82dd08f2a2
commit 4bc84b5399
17 changed files with 1178 additions and 633 deletions

View File

@@ -4,13 +4,7 @@
import { actionToValue } from "$lib/chord-editor/action-serializer";
import { actionPlugin } from "$lib/chord-editor/action-plugin";
import { delimPlugin } from "$lib/chord-editor/chord-delim-plugin";
import {
drawSelection,
dropCursor,
highlightActiveLine,
highlightSpecialChars,
keymap,
} from "@codemirror/view";
import { highlightActiveLine, keymap } from "@codemirror/view";
import { history, standardKeymap } from "@codemirror/commands";
import "$lib/chord-editor/chords.grammar";
import {
@@ -18,20 +12,32 @@
chordLanguageSupport,
} from "$lib/chord-editor/chords-grammar-plugin";
import { syntaxHighlighting } from "@codemirror/language";
import { autocompletion } from "@codemirror/autocomplete";
import { persistentWritable } from "$lib/storage";
import ActionList from "$lib/components/layout/ActionList.svelte";
import { actionAutocompletePlugin } from "$lib/chord-editor/autocomplete";
import {
actionLinter,
actionLinterDependencies,
} from "$lib/chord-editor/action-linter";
import { forceLinting } from "@codemirror/lint";
import { untrack } from "svelte";
import { splitCompound } from "$lib/serial/chord";
let queryFilter: string | undefined = $state(undefined);
const rawCode = persistentWritable("chord-editor-raw-code", false);
const showEdits = persistentWritable("chord-editor-show-edits", true);
const denseSpacing = persistentWritable("chord-editor-spacing", false);
let originalDoc = $derived(
$chords
.map((chord) => {
const [actions, compound] = splitCompound(chord.actions);
return (
chord.actions
.filter((it) => it !== 0)
.map((it) => actionToValue(it))
.join("") +
(compound
? "<0x" + compound.toString(16).padStart(8, "0") + ">"
: "") +
actions.map((it) => actionToValue(it)).join("") +
"=>" +
chord.phrase.map((it) => actionToValue(it)).join("")
);
@@ -49,31 +55,60 @@
extensions: [
...($rawCode ? [] : [delimPlugin, actionPlugin]),
chordLanguageSupport(),
autocompletion({ icons: false, selectOnOpen: true }),
actionLinter,
// lineNumbers(),
actionAutocompletePlugin((query) => {
queryFilter = query;
}),
history(),
dropCursor(),
syntaxHighlighting(chordHighlightStyle),
highlightActiveLine(),
drawSelection(),
highlightSpecialChars(),
// drawSelection(),
EditorView.theme({
".cm-line": {
borderBottom: "1px solid transparent",
caretColor: "var(--md-sys-color-on-surface)",
},
".cm-scroller": {
overflow: "auto",
width: "100%",
fontFamily: "inherit !important",
gap: "8px",
},
".cm-content": {
width: "100%",
},
".cm-cursor": {
borderColor: "var(--md-sys-color-on-surface)",
},
}),
keymap.of(standardKeymap),
],
});
return () => view.destroy();
});
$effect(() => {
$actionLinterDependencies;
untrack(() => view && forceLinting(view));
});
</script>
<label><input type="checkbox" bind:checked={$rawCode} />View as code</label>
<label><input type="checkbox" bind:checked={$showEdits} />Show edits</label>
<label><input type="checkbox" bind:checked={$rawCode} />Edit as code</label>
<!--<label><input type="checkbox" bind:checked={$showEdits} />Show edits</label>-->
<label
><input type="checkbox" bind:checked={$denseSpacing} />Dense Spacing</label
>
<div class="split">
<ActionList />
<div
class="editor"
class:hide-edits={!$showEdits}
class:raw={$rawCode}
class:dense-spacing={$denseSpacing}
bind:this={editor}
></div>
<ActionList {queryFilter} ignoreIcon={$rawCode} />
</div>
<style lang="scss">
@@ -82,21 +117,17 @@
gap: 1rem;
height: 100%;
> :global(:first-child) {
max-width: 600px;
> :global(:last-child) {
width: min(600px, 30vw);
}
}
.editor:not(.raw) :global(.cm-line) {
width: fit-content;
}
.editor :global(.cm-deletedChunk) {
opacity: 0.2;
}
.editor {
min-width: 600px;
width: min(600px, 30vw);
height: 100%;
font-size: 16px;
@@ -122,6 +153,40 @@
}
}
&:not(.raw) :global(.cm-line) {
columns: 2;
text-align: center;
}
&.dense-spacing :global(.cm-line) {
padding-block: 0;
}
:global(.cm-line) {
padding-block: 8px;
width: 100%;
text-wrap: wrap;
white-space: pre-wrap;
word-break: break-word;
> :global(*) {
break-before: avoid;
break-after: avoid;
break-inside: avoid;
}
}
:global(.chord-ignored) {
opacity: 0.5;
background-image: none;
text-decoration: line-through;
}
:global(.chord-invalid) {
color: var(--md-sys-color-error);
text-decoration-color: var(--md-sys-color-error);
}
:global(.change-button) {
height: 24px;
font-size: 16px;
@@ -144,29 +209,20 @@
}
:global(.cm-gutters) {
border: none;
border-color: transparent;
background-color: transparent;
}
&.raw :global(.cm-gutters) {
border-color: var(--md-sys-color-surface-variant);
background-color: var(--md-sys-color-surface);
}
:global(.cm-editor) {
outline: none;
height: 100%;
}
:global(.cm-line) {
border-bottom: 1px solid transparent;
line-height: 3em;
}
:global(.cm-scroller) {
overflow: auto;
font-family: inherit !important;
}
:global(.cm-cursor) {
border-color: var(--md-sys-color-on-surface);
}
:global(.cm-changedLine) {
background-color: color-mix(
in srgb,
@@ -182,17 +238,13 @@
:global(.cm-activeLine) {
border-bottom: 1px solid var(--md-sys-color-surface-variant);
/*background-color: color-mix(
in srgb,
var(--md-sys-color-surface-variant) 40%,
transparent
) !important;*/
&:not(.cm-changedLine) {
background-color: transparent !important;
}
}
:global(::selection),
:global(.cm-selectionBackground) {
background-color: var(--md-sys-color-surface-variant) !important;
}