mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-02-15 05:32:40 +00:00
feat: changes
This commit is contained in:
@@ -10,19 +10,15 @@ import Action from "$lib/components/Action.svelte";
|
|||||||
import type { Range } from "@codemirror/state";
|
import type { Range } from "@codemirror/state";
|
||||||
import { parsedChordsField } from "./parsed-chords-plugin";
|
import { parsedChordsField } from "./parsed-chords-plugin";
|
||||||
import { iterActions } from "./parse-meta";
|
import { iterActions } from "./parse-meta";
|
||||||
|
import type { KeyInfo } from "$lib/serial/keymap-codes";
|
||||||
|
|
||||||
export class ActionWidget extends WidgetType {
|
export class ActionWidget extends WidgetType {
|
||||||
component?: {};
|
component?: {};
|
||||||
|
|
||||||
constructor(readonly id: string | number) {
|
constructor(readonly info: KeyInfo) {
|
||||||
super();
|
super();
|
||||||
this.id = id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*override eq(other: ActionWidget) {
|
|
||||||
return this.id == other.id;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
toDOM() {
|
toDOM() {
|
||||||
if (this.component) {
|
if (this.component) {
|
||||||
unmount(this.component);
|
unmount(this.component);
|
||||||
@@ -32,15 +28,16 @@ export class ActionWidget extends WidgetType {
|
|||||||
|
|
||||||
this.component = mount(Action, {
|
this.component = mount(Action, {
|
||||||
target: element,
|
target: element,
|
||||||
props: { action: this.id, display: "keys", inText: true },
|
props: {
|
||||||
|
action: this.info,
|
||||||
|
display: "keys",
|
||||||
|
inText: true,
|
||||||
|
withPopover: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
override ignoreEvent() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
override destroy() {
|
override destroy() {
|
||||||
if (this.component) {
|
if (this.component) {
|
||||||
unmount(this.component);
|
unmount(this.component);
|
||||||
@@ -63,7 +60,7 @@ function actionWidgets(view: EditorView) {
|
|||||||
}
|
}
|
||||||
if (action.info && action.explicit) {
|
if (action.info && action.explicit) {
|
||||||
const deco = Decoration.replace({
|
const deco = Decoration.replace({
|
||||||
widget: new ActionWidget(action.code),
|
widget: new ActionWidget(action.info),
|
||||||
});
|
});
|
||||||
widgets.push(deco.range(action.range[0], action.range[1]));
|
widgets.push(deco.range(action.range[0], action.range[1]));
|
||||||
}
|
}
|
||||||
|
|||||||
41
src/lib/chord-editor/action-tooltip.ts
Normal file
41
src/lib/chord-editor/action-tooltip.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { hoverTooltip } from "@codemirror/view";
|
||||||
|
import { parsedChordsField } from "./parsed-chords-plugin";
|
||||||
|
import { type ActionMeta, iterActions } from "./parse-meta";
|
||||||
|
import { mount, unmount } from "svelte";
|
||||||
|
import ActionTooltip from "$lib/components/action/ActionTooltip.svelte";
|
||||||
|
|
||||||
|
function inRange(pos: number, side: 1 | -1, range: [number, number]) {
|
||||||
|
if (side < 0) {
|
||||||
|
return pos > range[0] && pos <= range[1];
|
||||||
|
} else {
|
||||||
|
return pos >= range[0] && pos < range[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const actionHover = hoverTooltip((view, pos, side) => {
|
||||||
|
const chord = view.state
|
||||||
|
.field(parsedChordsField)
|
||||||
|
.chords.find((chord) => inRange(pos, side, chord.range));
|
||||||
|
if (!chord) return null;
|
||||||
|
let action = iterActions<ActionMeta>(chord, (action) =>
|
||||||
|
inRange(pos, side, action.range) ? action : undefined,
|
||||||
|
);
|
||||||
|
if (!action?.info) return null;
|
||||||
|
return {
|
||||||
|
pos: action.range[0],
|
||||||
|
end: action.range[1],
|
||||||
|
create() {
|
||||||
|
const dom = document.createElement("div");
|
||||||
|
const element = mount(ActionTooltip, {
|
||||||
|
target: dom,
|
||||||
|
props: { info: action.info, valid: true },
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
dom,
|
||||||
|
destroy() {
|
||||||
|
unmount(element);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -152,25 +152,35 @@ export function mapParseResult(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function iterActions(
|
export function iterActions<T = void>(
|
||||||
chord: ChordMeta,
|
chord: ChordMeta,
|
||||||
callback: (action: ActionMeta) => void,
|
callback: (action: ActionMeta) => T | void,
|
||||||
) {
|
): T | undefined {
|
||||||
if (chord.input) {
|
if (chord.input) {
|
||||||
for (const action of chord.input.actions) {
|
for (const action of chord.input.actions) {
|
||||||
callback(action);
|
const result = callback(action);
|
||||||
|
if (result !== undefined) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chord.compounds) {
|
if (chord.compounds) {
|
||||||
for (const compound of chord.compounds) {
|
for (const compound of chord.compounds) {
|
||||||
for (const action of compound.actions) {
|
for (const action of compound.actions) {
|
||||||
callback(action);
|
const result = callback(action);
|
||||||
|
if (result !== undefined) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chord.phrase) {
|
if (chord.phrase) {
|
||||||
for (const action of chord.phrase.actions) {
|
for (const action of chord.phrase.actions) {
|
||||||
callback(action);
|
const result = callback(action);
|
||||||
|
if (result !== undefined) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import { actionMetaPlugin } from "./action-meta-plugin";
|
|||||||
import { parsedChordsField } from "./parsed-chords-plugin";
|
import { parsedChordsField } from "./parsed-chords-plugin";
|
||||||
import { changesPanel } from "./changes-panel.svelte";
|
import { changesPanel } from "./changes-panel.svelte";
|
||||||
import { searchKeymap } from "@codemirror/search";
|
import { searchKeymap } from "@codemirror/search";
|
||||||
|
import { actionHover } from "./action-tooltip";
|
||||||
|
|
||||||
const serializedFields = {
|
const serializedFields = {
|
||||||
history: historyField,
|
history: historyField,
|
||||||
@@ -47,6 +48,7 @@ export function createConfig(params: EditorConfig) {
|
|||||||
actionMetaPlugin.plugin,
|
actionMetaPlugin.plugin,
|
||||||
deviceChordField,
|
deviceChordField,
|
||||||
parsedChordsField,
|
parsedChordsField,
|
||||||
|
actionHover,
|
||||||
changesPanel(),
|
changesPanel(),
|
||||||
lintGutter(),
|
lintGutter(),
|
||||||
params.rawCode ? [lineNumbers()] : [delimPlugin, actionPlugin],
|
params.rawCode ? [lineNumbers()] : [delimPlugin, actionPlugin],
|
||||||
|
|||||||
@@ -4,17 +4,20 @@
|
|||||||
import { osLayout } from "$lib/os-layout";
|
import { osLayout } from "$lib/os-layout";
|
||||||
import { isVerbose } from "./verbose-action";
|
import { isVerbose } from "./verbose-action";
|
||||||
import { actionTooltip } from "$lib/title";
|
import { actionTooltip } from "$lib/title";
|
||||||
|
import ActionTooltip from "./action/ActionTooltip.svelte";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
action,
|
action,
|
||||||
display,
|
display,
|
||||||
ignoreIcon = false,
|
ignoreIcon = false,
|
||||||
inText = false,
|
inText = false,
|
||||||
|
withPopover = true,
|
||||||
}: {
|
}: {
|
||||||
action: string | number | KeyInfo;
|
action: string | number | KeyInfo;
|
||||||
display: "inline-keys" | "keys" | "verbose";
|
display: "inline-keys" | "keys" | "verbose";
|
||||||
ignoreIcon?: boolean;
|
ignoreIcon?: boolean;
|
||||||
inText?: boolean;
|
inText?: boolean;
|
||||||
|
withPopover?: boolean;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
let retrievedInfo = $derived(
|
let retrievedInfo = $derived(
|
||||||
@@ -35,39 +38,13 @@
|
|||||||
let icon = $derived(ignoreIcon ? undefined : info.icon);
|
let icon = $derived(ignoreIcon ? undefined : info.icon);
|
||||||
let dynamicMapping = $derived(info.keyCode && $osLayout.get(info.keyCode));
|
let dynamicMapping = $derived(info.keyCode && $osLayout.get(info.keyCode));
|
||||||
let hasPopover = $derived(
|
let hasPopover = $derived(
|
||||||
!retrievedInfo || !info.id || info.title || info.description,
|
withPopover &&
|
||||||
|
(!retrievedInfo || !info.id || info.title || info.description),
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet popover()}
|
{#snippet popover()}
|
||||||
{#if retrievedInfo}
|
<ActionTooltip valid={!!retrievedInfo} {info} />
|
||||||
{#if info.icon || info.display || !info.id}
|
|
||||||
<<b>{info.id ?? `0x${info.code.toString(16)}`}</b>>
|
|
||||||
{/if}
|
|
||||||
{#if info.title}
|
|
||||||
{info.title}
|
|
||||||
{/if}
|
|
||||||
{#if info.variant === "left"}
|
|
||||||
(Left)
|
|
||||||
{:else if info.variant === "right"}
|
|
||||||
(Right)
|
|
||||||
{/if}
|
|
||||||
{#if info.description}
|
|
||||||
<br />
|
|
||||||
<small>{info.description}</small>
|
|
||||||
{/if}
|
|
||||||
{#if info.breaking}
|
|
||||||
<br /> <i>Prevents prepended autospaces</i>
|
|
||||||
{/if}
|
|
||||||
{#if info.separator || info.breaking}
|
|
||||||
<br /> <i>Stops autocorrect</i>
|
|
||||||
{/if}
|
|
||||||
{:else}
|
|
||||||
<b>Unknown Action</b><br />
|
|
||||||
{#if info.code > 1023}
|
|
||||||
This action cannot be translated and will be ingored.
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#snippet kbdText()}
|
{#snippet kbdText()}
|
||||||
|
|||||||
34
src/lib/components/action/ActionTooltip.svelte
Normal file
34
src/lib/components/action/ActionTooltip.svelte
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { KeyInfo } from "$lib/serial/keymap-codes";
|
||||||
|
|
||||||
|
let { valid, info }: { valid: boolean; info: KeyInfo } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if valid}
|
||||||
|
{#if info.icon || info.display || !info.id}
|
||||||
|
<<b>{info.id ?? `0x${info.code.toString(16)}`}</b>>
|
||||||
|
{/if}
|
||||||
|
{#if info.title}
|
||||||
|
{info.title}
|
||||||
|
{/if}
|
||||||
|
{#if info.variant === "left"}
|
||||||
|
(Left)
|
||||||
|
{:else if info.variant === "right"}
|
||||||
|
(Right)
|
||||||
|
{/if}
|
||||||
|
{#if info.description}
|
||||||
|
<br />
|
||||||
|
<small>{info.description}</small>
|
||||||
|
{/if}
|
||||||
|
{#if info.breaking}
|
||||||
|
<br /> <i>Prevents prepended autospaces</i>
|
||||||
|
{/if}
|
||||||
|
{#if info.separator || info.breaking}
|
||||||
|
<br /> <i>Stops autocorrect</i>
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<b>Unknown Action</b><br />
|
||||||
|
{#if info.code > 1023}
|
||||||
|
This action cannot be translated and will be ingored.
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
@@ -158,7 +158,7 @@ export class CharaDevice {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly port: SerialPortLike,
|
readonly port: SerialPortLike,
|
||||||
public baudRate = 115200,
|
public baudRate = navigator.userAgent.includes("Mac") ? 38400 : 115200,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
@@ -621,9 +621,8 @@ export class CharaDevice {
|
|||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
writer.releaseLock();
|
writer.releaseLock();
|
||||||
|
await this.suspend();
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.suspend();
|
|
||||||
} finally {
|
} finally {
|
||||||
delete this.lock;
|
delete this.lock;
|
||||||
resolveLock!(true);
|
resolveLock!(true);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
} from "$lib/serial/connection";
|
} from "$lib/serial/connection";
|
||||||
import { fade, slide } from "svelte/transition";
|
import { fade, slide } from "svelte/transition";
|
||||||
import ConnectPopup from "./ConnectPopup.svelte";
|
import ConnectPopup from "./ConnectPopup.svelte";
|
||||||
|
import { goto } from "$app/navigation";
|
||||||
|
|
||||||
let locale = $state(
|
let locale = $state(
|
||||||
(browser && (localStorage.getItem("locale") as Locales)) || detectLocale(),
|
(browser && (localStorage.getItem("locale") as Locales)) || detectLocale(),
|
||||||
@@ -49,6 +50,8 @@
|
|||||||
function disconnect(event: MouseEvent) {
|
function disconnect(event: MouseEvent) {
|
||||||
if (event.shiftKey) {
|
if (event.shiftKey) {
|
||||||
sync();
|
sync();
|
||||||
|
} else if (event.altKey) {
|
||||||
|
goto("/terminal/");
|
||||||
} else {
|
} else {
|
||||||
$serialPort?.close();
|
$serialPort?.close();
|
||||||
$serialPort = undefined;
|
$serialPort = undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user