Skip to content

Commit 662a5f8

Browse files
committed
feat: cv2
1 parent 7d7c432 commit 662a5f8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1034
-3439
lines changed

icons.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const config = {
66
icons: [
77
"rocket_launch",
88
"deployed_code_update",
9+
"difference",
910
"adjust",
1011
"add",
1112
"piano",

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"build:tauri": "tauri build",
2626
"tauri": "tauri",
2727
"test": "vitest run --coverage",
28+
"test:chord-sync": "vitest chord-sync",
2829
"preview": "vite preview",
2930
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
3031
"minify-icons": "node src/tools/minify-icon-font.js",
@@ -41,6 +42,7 @@
4142
"@codemirror/language": "^6.12.1",
4243
"@codemirror/lint": "^6.9.2",
4344
"@codemirror/merge": "^6.11.2",
45+
"@codemirror/search": "^6.6.0",
4446
"@codemirror/state": "^6.5.3",
4547
"@codemirror/view": "^6.39.9",
4648
"@fontsource-variable/material-symbols-rounded": "^5.2.30",

pnpm-lock.yaml

Lines changed: 7 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/lib/backup/backup.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,9 @@ import type {
66
CharaSettingsFile,
77
} from "$lib/share/chara-file.js";
88
import type { Change } from "$lib/undo-redo.js";
9-
import {
10-
changes,
11-
ChangeType,
12-
chords,
13-
layout,
14-
settings,
15-
} from "$lib/undo-redo.js";
9+
import { changes, ChangeType, layout, settings } from "$lib/undo-redo.js";
1610
import { get } from "svelte/store";
17-
import { activeProfile, serialPort } from "../serial/connection";
11+
import { activeProfile, deviceChords, serialPort } from "../serial/connection";
1812
import { csvLayoutToJson, isCsvLayout } from "$lib/backup/compat/legacy-layout";
1913
import { isCsvChords, csvChordsToJson } from "./compat/legacy-chords";
2014

@@ -60,7 +54,7 @@ export function createChordBackup(): CharaChordFile {
6054
return {
6155
charaVersion: 1,
6256
type: "chords",
63-
chords: get(chords).map((it) => [it.actions, it.phrase]),
57+
chords: get(deviceChords).map((it) => [it.actions, it.phrase]),
6458
};
6559
}
6660

@@ -168,7 +162,9 @@ export function restoreFromFile(
168162
export function getChangesFromChordFile(file: CharaChordFile) {
169163
const changes: Change[] = [];
170164
const existingChords = new Set(
171-
get(chords).map(({ phrase, actions }) => JSON.stringify([actions, phrase])),
165+
get(deviceChords).map(({ phrase, actions }) =>
166+
JSON.stringify([actions, phrase]),
167+
),
172168
);
173169
for (const [input, output] of file.chords) {
174170
if (existingChords.has(JSON.stringify([input, output]))) {
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<script lang="ts">
2+
import {
3+
serialPort,
4+
sync,
5+
syncProgress,
6+
syncStatus,
7+
} from "$lib/serial/connection";
8+
import type { ParseResult } from "./parse-meta";
9+
import { actionTooltip } from "$lib/title";
10+
import LL from "$i18n/i18n-svelte";
11+
import ProgressButton from "$lib/ProgressButton.svelte";
12+
import type { EditorView } from "codemirror";
13+
import { createSaveTask } from "./save-chords";
14+
import { goto } from "$app/navigation";
15+
16+
let { parsed, view }: { parsed: ParseResult; view: EditorView } = $props();
17+
18+
$inspect(parsed);
19+
20+
let added = $derived(
21+
parsed.chords.reduce(
22+
(acc, chord) =>
23+
acc +
24+
(chord.phrase && chord.phrase.originalValue === undefined ? 1 : 0),
25+
0,
26+
),
27+
);
28+
29+
let changed = $derived(
30+
parsed.chords.reduce(
31+
(acc, chord) =>
32+
acc +
33+
(chord.phrase?.originalValue !== undefined &&
34+
chord.phrase.originalValue !== chord.phrase.value
35+
? 1
36+
: 0),
37+
0,
38+
),
39+
);
40+
41+
let error: Error | undefined = $state(undefined);
42+
43+
async function save() {
44+
const port = $serialPort;
45+
if (!view || !port) return;
46+
error = undefined;
47+
const task = createSaveTask(view);
48+
const total = task.remove.length + task.set.length;
49+
$syncStatus = "uploading";
50+
$syncProgress = { current: 0, max: total };
51+
let progressCount = 0;
52+
for (const input of task.remove) {
53+
try {
54+
await port.deleteChord({ actions: input });
55+
} catch (e) {
56+
error = e as Error;
57+
}
58+
progressCount++;
59+
$syncProgress = { current: progressCount, max: total };
60+
}
61+
for (const [input, phrase] of task.set) {
62+
try {
63+
await port.setChord({ actions: input, phrase });
64+
} catch (e) {
65+
error = e as Error;
66+
}
67+
progressCount++;
68+
$syncProgress = { current: progressCount, max: total };
69+
}
70+
if (error !== undefined) {
71+
goto("/terminal");
72+
}
73+
await sync();
74+
}
75+
76+
let removed = $derived(parsed.removed.length);
77+
</script>
78+
79+
<div class="container">
80+
{#if added + changed + removed !== 0 || $syncStatus === "uploading" || $syncStatus === "error"}
81+
<div {@attach actionTooltip($LL.saveActions.SAVE())}>
82+
<ProgressButton
83+
disabled={$syncStatus !== "done"}
84+
working={$syncStatus === "uploading" || $syncStatus === "downloading"}
85+
progress={$syncProgress && $syncStatus === "uploading"
86+
? $syncProgress.current / $syncProgress.max
87+
: 0}
88+
style="--height: 36px"
89+
error={error !== undefined
90+
? (error.message ?? error.toString())
91+
: undefined}
92+
onclick={save}
93+
>
94+
<span class="icon">save</span>
95+
{$LL.saveActions.SAVE()}
96+
</ProgressButton>
97+
</div>
98+
{/if}
99+
100+
<div>
101+
{#if added}
102+
<span class="added">+{added}</span>
103+
{/if}
104+
{#if changed}
105+
<span class="changed">~{changed}</span>
106+
{/if}
107+
{#if removed}
108+
<span class="removed">-{removed}</span>
109+
{/if}
110+
</div>
111+
112+
{#if parsed.aliases.size > 0}
113+
<div class="section">
114+
<span class="icon">content_copy</span>
115+
<span>{parsed.aliases.size}</span>
116+
</div>
117+
{/if}
118+
</div>
119+
120+
<style lang="scss">
121+
.icon {
122+
font-size: 16px;
123+
}
124+
125+
.container {
126+
display: flex;
127+
justify-content: flex-start;
128+
align-items: center;
129+
gap: 32px;
130+
}
131+
132+
.section {
133+
display: flex;
134+
align-items: center;
135+
gap: 8px;
136+
}
137+
138+
.added {
139+
color: var(--md-sys-color-success);
140+
}
141+
142+
.changed {
143+
color: var(--md-sys-color-warning);
144+
}
145+
146+
.removed {
147+
color: var(--md-sys-color-error);
148+
}
149+
</style>

src/lib/chord-editor/action-linter.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,27 @@ export function actionLinter(config?: Parameters<typeof linter>[1]) {
128128
message: `Phrase changed`,
129129
});
130130
}
131+
132+
if (chord.aliases) {
133+
diagnostics.push({
134+
from: chord.phrase.range[0],
135+
to: chord.phrase.range[1],
136+
severity: "warning",
137+
markClass: "chord-alias",
138+
message: `Alias of ${chord.aliases.length} chord(s)`,
139+
actions: chord.aliases.map((alias) => ({
140+
name: `Go to ${view.state.doc.sliceString(alias.range[0], alias.input?.range[1] ?? alias.range[1])}`,
141+
apply(view) {
142+
view.dispatch({
143+
selection: {
144+
anchor: alias.range[0],
145+
},
146+
scrollIntoView: true,
147+
});
148+
},
149+
})),
150+
});
151+
}
131152
}
132153
}
133154
return diagnostics;

0 commit comments

Comments
 (0)