mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-01-21 09:23:00 +00:00
Compare commits
16 Commits
v1.5.0
...
72a8e084ce
| Author | SHA1 | Date | |
|---|---|---|---|
|
72a8e084ce
|
|||
|
989e844190
|
|||
|
500221f39a
|
|||
|
|
d91273d27b | ||
|
888df6dd66
|
|||
|
7ad9612037
|
|||
|
3f9674b399
|
|||
|
92ba5bcb24
|
|||
|
2163a63a7c
|
|||
|
65a5a2517e
|
|||
|
21e8c291b0
|
|||
|
4106a80d53
|
|||
|
|
01fb61d27c | ||
|
3dd91a1cea
|
|||
|
cbcf705f71
|
|||
|
4007810c7b
|
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@@ -21,18 +21,22 @@ jobs:
|
|||||||
- name: ⏬ Install Python dependencies
|
- name: ⏬ Install Python dependencies
|
||||||
run: pip install -r requirements.txt
|
run: pip install -r requirements.txt
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
- name: 🐉 Use Node.js 18.16.x
|
- name: 🐉 Use Node.js 18.16.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18.16.x
|
node-version: 18.16.x
|
||||||
cache: "npm"
|
cache: "pnpm"
|
||||||
- name: ⏬ Install Node dependencies
|
- name: ⏬ Install Node dependencies
|
||||||
run: npm ci
|
run: pnpm install
|
||||||
|
|
||||||
- name: 🔥 Optimize icon font
|
- name: 🔥 Optimize icon font
|
||||||
run: npm run minify-icons
|
run: pnpm minify-icons
|
||||||
- name: 🔨 Build site
|
- name: 🔨 Build site
|
||||||
run: npm run build
|
run: pnpm build
|
||||||
|
|
||||||
- name: 📦 Upload build artifacts
|
- name: 📦 Upload build artifacts
|
||||||
uses: actions/upload-artifact@v3.1.2
|
uses: actions/upload-artifact@v3.1.2
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ You may need to run through some additional setup to get Rust running inside Int
|
|||||||
- Python >=3.10
|
- Python >=3.10
|
||||||
- Rust Stable (For Tauri Development)
|
- Rust Stable (For Tauri Development)
|
||||||
|
|
||||||
I know, python in JS projects is extremely annoying, unfortunately,
|
I know, python in JS projects is extremely annoying. Unfortunately,
|
||||||
it seems to be the only platform that offers a functional
|
it seems to be the only platform that offers a functional
|
||||||
way to subset variable woff2 fonts with ligatures.
|
way to subset variable woff2 fonts with ligatures.
|
||||||
|
|
||||||
|
|||||||
117
flake.nix
117
flake.nix
@@ -4,56 +4,75 @@
|
|||||||
rust-overlay.url = "github:oxalica/rust-overlay";
|
rust-overlay.url = "github:oxalica/rust-overlay";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
};
|
};
|
||||||
outputs = {
|
outputs =
|
||||||
self,
|
{
|
||||||
nixpkgs,
|
self,
|
||||||
flake-utils,
|
nixpkgs,
|
||||||
rust-overlay,
|
flake-utils,
|
||||||
}:
|
rust-overlay,
|
||||||
flake-utils.lib.eachDefaultSystem (system: let
|
}:
|
||||||
overlays = [(import rust-overlay)];
|
flake-utils.lib.eachDefaultSystem (
|
||||||
pkgs = import nixpkgs {inherit system overlays;};
|
system:
|
||||||
rust-bin = pkgs.rust-bin.stable.latest.default.override {
|
let
|
||||||
extensions = ["rust-src" "rust-std" "clippy" "rust-analyzer"];
|
overlays = [ (import rust-overlay) ];
|
||||||
};
|
pkgs = import nixpkgs { inherit system overlays; };
|
||||||
fontMin = pkgs.python311.withPackages (ps: with ps; [brotli fonttools] ++ (with fonttools.optional-dependencies; [woff]));
|
rust-bin = pkgs.rust-bin.stable.latest.default.override {
|
||||||
tauriPkgs = nixpkgs.legacyPackages.${system};
|
extensions = [
|
||||||
libraries = with tauriPkgs; [
|
"rust-src"
|
||||||
webkitgtk
|
"rust-std"
|
||||||
gtk3
|
"clippy"
|
||||||
cairo
|
"rust-analyzer"
|
||||||
gdk-pixbuf
|
];
|
||||||
glib
|
};
|
||||||
dbus
|
fontMin = pkgs.python311.withPackages (
|
||||||
openssl_3
|
ps:
|
||||||
librsvg
|
with ps;
|
||||||
];
|
[
|
||||||
packages =
|
brotli
|
||||||
(with pkgs; [
|
fonttools
|
||||||
nodejs_18
|
]
|
||||||
rust-bin
|
++ (with fonttools.optional-dependencies; [ woff ])
|
||||||
fontMin
|
);
|
||||||
])
|
tauriPkgs = nixpkgs.legacyPackages.${system};
|
||||||
++ (with tauriPkgs; [
|
libraries = with tauriPkgs; [
|
||||||
curl
|
webkitgtk
|
||||||
wget
|
gtk3
|
||||||
pkg-config
|
cairo
|
||||||
|
gdk-pixbuf
|
||||||
|
glib
|
||||||
dbus
|
dbus
|
||||||
openssl_3
|
openssl_3
|
||||||
glib
|
|
||||||
gtk3
|
|
||||||
libsoup
|
|
||||||
webkitgtk
|
|
||||||
librsvg
|
librsvg
|
||||||
# serial plugin
|
];
|
||||||
udev
|
packages =
|
||||||
]);
|
(with pkgs; [
|
||||||
in {
|
nodejs_18
|
||||||
devShell = pkgs.mkShell {
|
nodePackages.pnpm
|
||||||
buildInputs = packages;
|
rust-bin
|
||||||
shellHook = ''
|
fontMin
|
||||||
export LD_LIBRARY_PATH=${pkgs.lib.makeLibraryPath libraries}:$LD_LIBRARY_PATH
|
])
|
||||||
'';
|
++ (with tauriPkgs; [
|
||||||
};
|
curl
|
||||||
});
|
wget
|
||||||
|
pkg-config
|
||||||
|
dbus
|
||||||
|
openssl_3
|
||||||
|
glib
|
||||||
|
gtk3
|
||||||
|
libsoup
|
||||||
|
webkitgtk
|
||||||
|
librsvg
|
||||||
|
# serial plugin
|
||||||
|
udev
|
||||||
|
]);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShell = pkgs.mkShell {
|
||||||
|
buildInputs = packages;
|
||||||
|
shellHook = ''
|
||||||
|
export LD_LIBRARY_PATH=${pkgs.lib.makeLibraryPath libraries}:$LD_LIBRARY_PATH
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ const config = {
|
|||||||
upload_2: "ff52",
|
upload_2: "ff52",
|
||||||
stat_minus_2: "e69c",
|
stat_minus_2: "e69c",
|
||||||
stat_2: "e699",
|
stat_2: "e699",
|
||||||
|
routine: "e20c",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
11684
package-lock.json
generated
11684
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
67
package.json
67
package.json
@@ -1,8 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "charachorder-device-manager",
|
"name": "charachorder-device-manager",
|
||||||
"version": "1.5.0",
|
"version": "1.5.2",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.16",
|
||||||
|
"pnpm": ">=8.6"
|
||||||
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/CharaChorder/DeviceManager.git"
|
"url": "https://github.com/CharaChorder/DeviceManager.git"
|
||||||
@@ -30,52 +34,55 @@
|
|||||||
"typesafe-i18n": "typesafe-i18n"
|
"typesafe-i18n": "typesafe-i18n"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@codemirror/autocomplete": "^6.15.0",
|
"@codemirror/autocomplete": "^6.17.0",
|
||||||
"@codemirror/commands": "^6.3.3",
|
"@codemirror/commands": "^6.6.0",
|
||||||
"@codemirror/lang-javascript": "^6.2.2",
|
"@codemirror/lang-javascript": "^6.2.2",
|
||||||
"@codemirror/language": "^6.10.1",
|
"@codemirror/language": "^6.10.2",
|
||||||
"@codemirror/state": "^6.4.1",
|
"@codemirror/state": "^6.4.1",
|
||||||
"@fontsource-variable/material-symbols-rounded": "^5.0.27",
|
"@codemirror/view": "^6.28.4",
|
||||||
"@fontsource-variable/noto-sans-mono": "^5.0.19",
|
"@fontsource-variable/material-symbols-rounded": "^5.0.34",
|
||||||
"@material/material-color-utilities": "^0.2.7",
|
"@fontsource-variable/noto-sans-mono": "^5.0.20",
|
||||||
|
"@lezer/highlight": "^1.2.0",
|
||||||
|
"@material/material-color-utilities": "^0.3.0",
|
||||||
"@modyfi/vite-plugin-yaml": "^1.1.0",
|
"@modyfi/vite-plugin-yaml": "^1.1.0",
|
||||||
"@sveltejs/adapter-static": "^2.0.3",
|
"@sveltejs/adapter-static": "^3.0.2",
|
||||||
"@sveltejs/kit": "^1.30.4",
|
"@sveltejs/kit": "^2.5.18",
|
||||||
"@sveltejs/vite-plugin-svelte": "^2.5.3",
|
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
||||||
"@tauri-apps/api": "^1.5.3",
|
"@tauri-apps/api": "^1.6.0",
|
||||||
"@tauri-apps/cli": "^1.5.11",
|
"@tauri-apps/cli": "^1.6.0",
|
||||||
"@types/dom-view-transitions": "^1.0.4",
|
"@types/dom-view-transitions": "^1.0.4",
|
||||||
"@types/flexsearch": "^0.7.6",
|
"@types/flexsearch": "^0.7.6",
|
||||||
"@types/w3c-web-serial": "^1.0.6",
|
"@types/w3c-web-serial": "^1.0.6",
|
||||||
"@types/w3c-web-usb": "^1.0.10",
|
"@types/w3c-web-usb": "^1.0.10",
|
||||||
"@vite-pwa/sveltekit": "^0.2.10",
|
"@vite-pwa/sveltekit": "^0.6.0",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"cypress": "^13.7.2",
|
"cypress": "^13.13.0",
|
||||||
"flexsearch": "^0.7.43",
|
"flexsearch": "^0.7.43",
|
||||||
"fontkit": "^2.0.2",
|
"fontkit": "^2.0.2",
|
||||||
"glob": "^10.3.12",
|
"glob": "^10.4.3",
|
||||||
"jsdom": "^22.1.0",
|
"jsdom": "^24.1.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.3.2",
|
||||||
"prettier-plugin-svelte": "^3.2.2",
|
"prettier-plugin-svelte": "^3.2.5",
|
||||||
"sass": "^1.74.1",
|
"sass": "^1.77.6",
|
||||||
"stylelint": "^15.11.0",
|
"stylelint": "^16.6.1",
|
||||||
"stylelint-config-clean-order": "^5.4.2",
|
"stylelint-config-clean-order": "^6.1.0",
|
||||||
"stylelint-config-html": "^1.1.0",
|
"stylelint-config-html": "^1.1.0",
|
||||||
"stylelint-config-prettier-scss": "^1.0.0",
|
"stylelint-config-prettier-scss": "^1.0.0",
|
||||||
"stylelint-config-recommended-scss": "^13.1.0",
|
"stylelint-config-recommended-scss": "^14.0.0",
|
||||||
"stylelint-config-standard-scss": "^11.1.0",
|
"stylelint-config-standard-scss": "^13.1.0",
|
||||||
"svelte": "^4.2.12",
|
"svelte": "^4.2.18",
|
||||||
"svelte-check": "^3.6.9",
|
"svelte-check": "^3.8.4",
|
||||||
"svelte-preprocess": "^5.1.3",
|
"svelte-preprocess": "^6.0.1",
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"typesafe-i18n": "^5.26.2",
|
"typesafe-i18n": "^5.26.2",
|
||||||
"typescript": "^5.4.4",
|
"typescript": "^5.5.3",
|
||||||
"vite": "^4.5.3",
|
"vite": "^5.3.3",
|
||||||
"vite-plugin-mkcert": "^1.17.5",
|
"vite-plugin-mkcert": "^1.17.5",
|
||||||
"vite-plugin-pwa": "^0.17.5",
|
"vite-plugin-pwa": "^0.20.0",
|
||||||
"vitest": "^0.34.6"
|
"vitest": "^1.6.0",
|
||||||
|
"workbox-window": "^7.1.0"
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module"
|
||||||
}
|
}
|
||||||
|
|||||||
7074
pnpm-lock.yaml
generated
Normal file
7074
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "app"
|
name = "app"
|
||||||
version = "1.5.0"
|
version = "1.5.2"
|
||||||
description = "A Tauri App"
|
description = "A Tauri App"
|
||||||
authors = ["Thea Schöbl <dev@theaninova.de>"]
|
authors = ["Thea Schöbl <dev@theaninova.de>"]
|
||||||
license = "AGPL-3"
|
license = "AGPL-3"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"devPath": "http://localhost:5173",
|
"devPath": "http://localhost:5173",
|
||||||
"distDir": "../build"
|
"distDir": "../build"
|
||||||
},
|
},
|
||||||
"package": { "productName": "amacc1ng", "version": "1.5.0" },
|
"package": { "productName": "amacc1ng", "version": "1.5.2" },
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": { "all": false },
|
"allowlist": { "all": false },
|
||||||
"bundle": {
|
"bundle": {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
name: CharaChorder
|
name: CharaChorder
|
||||||
description: CharaChorder specific actions
|
description: CharaChorder specific actions
|
||||||
actions:
|
actions:
|
||||||
|
0:
|
||||||
|
id: "NO_ACTION"
|
||||||
|
display: "No Action"
|
||||||
528:
|
528:
|
||||||
id: "RESTART"
|
id: "RESTART"
|
||||||
title: Restart Device
|
title: Restart Device
|
||||||
@@ -58,6 +61,7 @@ actions:
|
|||||||
544:
|
544:
|
||||||
variantOf: 36
|
variantOf: 36
|
||||||
id: "SPACERIGHT"
|
id: "SPACERIGHT"
|
||||||
|
display: " "
|
||||||
title: Right Spacebar (eg CC Lite)
|
title: Right Spacebar (eg CC Lite)
|
||||||
icon: space_bar
|
icon: space_bar
|
||||||
variant: right
|
variant: right
|
||||||
@@ -66,6 +70,9 @@ actions:
|
|||||||
title: Primary Keymap
|
title: Primary Keymap
|
||||||
icon: counter_1
|
icon: counter_1
|
||||||
variant: left
|
variant: left
|
||||||
|
description: |
|
||||||
|
Acts as a toggle if the same action is not assigned
|
||||||
|
to the target layer
|
||||||
549:
|
549:
|
||||||
variantOf: 548
|
variantOf: 548
|
||||||
<<: *primary_keymap
|
<<: *primary_keymap
|
||||||
@@ -76,6 +83,9 @@ actions:
|
|||||||
title: Numeric Layer
|
title: Numeric Layer
|
||||||
icon: counter_2
|
icon: counter_2
|
||||||
variant: left
|
variant: left
|
||||||
|
description: |
|
||||||
|
Acts as a toggle if the same action is not assigned
|
||||||
|
to the target layer
|
||||||
551:
|
551:
|
||||||
variantOf: 550
|
variantOf: 550
|
||||||
<<: *secondary_keymap
|
<<: *secondary_keymap
|
||||||
@@ -86,11 +96,34 @@ actions:
|
|||||||
title: Function Layer
|
title: Function Layer
|
||||||
icon: counter_3
|
icon: counter_3
|
||||||
variant: left
|
variant: left
|
||||||
|
description: |
|
||||||
|
Acts as a toggle if the same action is not assigned
|
||||||
|
to the target layer
|
||||||
553:
|
553:
|
||||||
variationOf: 552
|
variationOf: 552
|
||||||
<<: *tertiary_keymap
|
<<: *tertiary_keymap
|
||||||
id: "KM_3_R"
|
id: "KM_3_R"
|
||||||
variant: right
|
variant: right
|
||||||
|
558:
|
||||||
|
id: HOLD_COMPOUND
|
||||||
|
title: Activate Chord Library
|
||||||
|
icon: layers
|
||||||
|
description: |
|
||||||
|
When used in a chord includes that chord as a base
|
||||||
|
compound chord for all subsequent chords.
|
||||||
|
This is effectively a library switch.
|
||||||
|
Since library activations can be nested, you
|
||||||
|
usually add a "Reset Chord Library" before this action.
|
||||||
|
559:
|
||||||
|
id: RELEASE_COMPOUND
|
||||||
|
title: Reset Chord Library
|
||||||
|
icon: layers_clear
|
||||||
|
description: |
|
||||||
|
Releases the active compound state, returning
|
||||||
|
to the default library.
|
||||||
|
While "Activate Chord Library" can only be used
|
||||||
|
as an output of a chord, this action can be assigned
|
||||||
|
to switches directly.
|
||||||
576:
|
576:
|
||||||
id: ACTION_DELAY_1000
|
id: ACTION_DELAY_1000
|
||||||
icon: clock_loader_90
|
icon: clock_loader_90
|
||||||
|
|||||||
@@ -422,8 +422,8 @@ actions:
|
|||||||
title: Keyboard Non-US \ and | (US English)
|
title: Keyboard Non-US \ and | (US English)
|
||||||
357:
|
357:
|
||||||
id: "COMPOSE"
|
id: "COMPOSE"
|
||||||
|
icon: menu
|
||||||
title: Keyboard Application
|
title: Keyboard Application
|
||||||
description: Officially supported by Win, Unix, and Boot
|
|
||||||
358:
|
358:
|
||||||
id: "POWER"
|
id: "POWER"
|
||||||
keyCode: "Power"
|
keyCode: "Power"
|
||||||
@@ -944,99 +944,99 @@ actions:
|
|||||||
title: Keyboard Right GUI
|
title: Keyboard Right GUI
|
||||||
488:
|
488:
|
||||||
id: "KSC_E8"
|
id: "KSC_E8"
|
||||||
|
icon: play_pause
|
||||||
keyCode: "MediaPlayPause"
|
keyCode: "MediaPlayPause"
|
||||||
title: Media Play Pause
|
title: Media Play Pause
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
|
||||||
489:
|
489:
|
||||||
id: "KSC_E9"
|
id: "KSC_E9"
|
||||||
|
icon: stop
|
||||||
keyCode: "MediaStop"
|
keyCode: "MediaStop"
|
||||||
title: Media Stop CD
|
title: Media Stop CD
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
|
||||||
490:
|
490:
|
||||||
id: "KSC_EA"
|
id: "KSC_EA"
|
||||||
|
icon: skip_previous
|
||||||
keyCode: "MediaTrackPrevious"
|
keyCode: "MediaTrackPrevious"
|
||||||
title: Media Previous Song
|
title: Media Previous Song
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
|
||||||
491:
|
491:
|
||||||
id: "KSC_EB"
|
id: "KSC_EB"
|
||||||
|
icon: skip_next
|
||||||
keyCode: "MediaTrackNext"
|
keyCode: "MediaTrackNext"
|
||||||
title: Media Next Song
|
title: Media Next Song
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
|
||||||
492:
|
492:
|
||||||
id: "KSC_EC"
|
id: "KSC_EC"
|
||||||
|
icon: eject
|
||||||
keyCode: "Eject"
|
keyCode: "Eject"
|
||||||
title: Media Eject CD
|
title: Media Eject CD
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: MacOS only
|
||||||
493:
|
493:
|
||||||
id: "KSC_ED"
|
id: "KSC_ED"
|
||||||
|
icon: volume_up
|
||||||
keyCode: "AudioVolumeUp"
|
keyCode: "AudioVolumeUp"
|
||||||
title: Media Volume Up
|
title: Media Volume Up
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
|
||||||
494:
|
494:
|
||||||
id: "KSC_EE"
|
id: "KSC_EE"
|
||||||
|
icon: volume_down
|
||||||
keyCode: "AudioVolumeDown"
|
keyCode: "AudioVolumeDown"
|
||||||
title: Media Volume Down
|
title: Media Volume Down
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
|
||||||
495:
|
495:
|
||||||
id: "KSC_EF"
|
id: "KSC_EF"
|
||||||
|
icon: volume_off
|
||||||
keyCode: "AudioVolumeMute"
|
keyCode: "AudioVolumeMute"
|
||||||
title: Media Mute
|
title: Media Mute
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
|
||||||
496:
|
496:
|
||||||
id: "KSC_F0"
|
id: "KSC_F0"
|
||||||
title: Media www
|
icon: language
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
title: Media Browser
|
||||||
497:
|
497:
|
||||||
id: "KSC_F1"
|
id: "KSC_F1"
|
||||||
keyCode: "BrowserBack"
|
keyCode: "BrowserBack"
|
||||||
title: Media Back
|
title: Media Browser Back
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
|
||||||
498:
|
498:
|
||||||
id: "KSC_F2"
|
id: "KSC_F2"
|
||||||
keyCode: "BrowserForward"
|
keyCode: "BrowserForward"
|
||||||
title: Media Forward
|
title: Media Browser Forward
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
|
||||||
499:
|
499:
|
||||||
id: "KSC_F3"
|
id: "KSC_F3"
|
||||||
keyCode: "BrowserStop"
|
keyCode: "BrowserStop"
|
||||||
title: Media Stop
|
title: Media Browser Stop
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not supported on MacOS
|
||||||
500:
|
500:
|
||||||
id: "KSC_F4"
|
id: "KSC_F4"
|
||||||
|
icon: search
|
||||||
keyCode: "BrowserSearch"
|
keyCode: "BrowserSearch"
|
||||||
title: Media Find
|
title: Media Browser Search
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
|
||||||
501:
|
501:
|
||||||
id: "KSC_F5"
|
id: "KSC_F5"
|
||||||
title: Media Scroll Up
|
icon: brightness_high
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
title: Media Brightness Up
|
||||||
502:
|
502:
|
||||||
id: "KSC_F6"
|
id: "KSC_F6"
|
||||||
title: Media Scroll Down
|
icon: brightness_low
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
title: Media Brightness Down
|
||||||
503:
|
503:
|
||||||
id: "KSC_F7"
|
id: "KSC_F7"
|
||||||
title: Media Edit
|
title: Media Edit
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
504:
|
504:
|
||||||
id: "KSC_F8"
|
id: "KSC_F8"
|
||||||
|
icon: bedtime
|
||||||
keyCode: "Sleep"
|
keyCode: "Sleep"
|
||||||
title: Media Sleep
|
title: Media System Sleep
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not required to be supported by any OS. Possibly deprecated.
|
||||||
505:
|
505:
|
||||||
id: "KSC_F9"
|
id: "KSC_F9"
|
||||||
|
icon: routine
|
||||||
keyCode: "WakeUp"
|
keyCode: "WakeUp"
|
||||||
title: Media Coffee
|
title: Media System Wake
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not supported on Windows
|
||||||
506:
|
506:
|
||||||
id: "KSC_FA"
|
id: "KSC_FA"
|
||||||
keyCode: "BrowserRefresh"
|
keyCode: "BrowserRefresh"
|
||||||
title: Media Refresh
|
title: Media Browser Refresh
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
|
||||||
507:
|
507:
|
||||||
id: "KSC_FB"
|
id: "KSC_FB"
|
||||||
title: Media Calc
|
title: Media Calculator
|
||||||
description: Not required to be supported by any OS. Possibly deprecated.
|
description: Not supported on MacOS
|
||||||
508:
|
508:
|
||||||
id: "KSC_FC"
|
id: "KSC_FC"
|
||||||
description: Not required to be supported by any OS.
|
description: Not required to be supported by any OS.
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"You can use 'cursor warping' by adding arrow key actions to a chord, for example to chord '()' with the cursor in the middle of the brackets",
|
"You can use 'cursor warping' by adding arrow key actions to a chord, for example to chord '()' with the cursor in the middle of the brackets",
|
||||||
"An arpeggiate is a single key press that modifies the preceding chord. Modifiers can be arpeggiated",
|
"An arpeggiate is a single key press that modifies the preceding chord. Modifiers can be arpeggiated",
|
||||||
"Some actions are marked as a 'macro', which means the output is generated by a key sequence rather than a pure key press. Be cautious with those, as they can affect other keys when held together!",
|
"Some actions are marked as a 'macro', which means the output is generated by a key sequence rather than a pure key press. Be cautious with those, as they can affect other keys when held together!",
|
||||||
"Spurring is a chording only mode which is more advanced, but can greatly imporve typing speed when mastered",
|
"Spurring is a chording only mode which is more advanced, but can greatly improve typing speed when mastered",
|
||||||
"The forced chord phenomenon is when typing a word character by character starts to feel unnatural",
|
"The forced chord phenomenon is when typing a word character by character starts to feel unnatural",
|
||||||
"Don't be afraid to delete chords you keep getting wrong",
|
"Don't be afraid to delete chords you keep getting wrong",
|
||||||
"Most people find it easier to start their chord library from scratch rather than learning someone else's",
|
"Most people find it easier to start their chord library from scratch rather than learning someone else's",
|
||||||
@@ -32,5 +32,7 @@
|
|||||||
"You can use Nexus to track words you might want to add to your chord library",
|
"You can use Nexus to track words you might want to add to your chord library",
|
||||||
"The CC1 default layout was 80% science, 20% art",
|
"The CC1 default layout was 80% science, 20% art",
|
||||||
"There is little to no reason to use hjkl in VIM on a CC1 since the arrows keys are so close already",
|
"There is little to no reason to use hjkl in VIM on a CC1 since the arrows keys are so close already",
|
||||||
"The device manager automatically creates a backup for you when you reboot your device into the bootloader"
|
"The device manager automatically creates a backup for you when you reboot your device into the bootloader",
|
||||||
|
"You can use \"compound\", \"macro\", \"suffix\" and \"cursor warp\" in the chord search to find specific types of chords",
|
||||||
|
"You can search for chord inputs by using a leading \"+\", for example \"+a +DUP\" will show all chords with inputs that contain both a and DUP"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -96,7 +96,12 @@ export function restoreFromFile(
|
|||||||
case "backup": {
|
case "backup": {
|
||||||
const recent = file.history[0];
|
const recent = file.history[0];
|
||||||
if (!recent) return;
|
if (!recent) return;
|
||||||
if (recent[1].device !== get(serialPort)?.device) {
|
let backupDevice = recent[1].device;
|
||||||
|
if (backupDevice === "TWO") backupDevice = "ONE";
|
||||||
|
let currentDevice = get(serialPort)?.device;
|
||||||
|
if (currentDevice === "TWO") currentDevice = "ONE";
|
||||||
|
|
||||||
|
if (backupDevice !== currentDevice) {
|
||||||
alert("Backup is incompatible with this device");
|
alert("Backup is incompatible with this device");
|
||||||
throw new Error("Backup is incompatible with this device");
|
throw new Error("Backup is incompatible with this device");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import type { KeyInfo } from "$lib/serial/keymap-codes";
|
import type { KeyInfo } from "$lib/serial/keymap-codes";
|
||||||
import { action as title } from "$lib/title";
|
import { action as title } from "$lib/title";
|
||||||
import { osLayout } from "$lib/os-layout";
|
import { osLayout } from "$lib/os-layout";
|
||||||
import LL from "../../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
|
|
||||||
export let action: number | KeyInfo;
|
export let action: number | KeyInfo;
|
||||||
export let display: "inline-keys" | "keys" = "inline-keys";
|
export let display: "inline-keys" | "keys" = "inline-keys";
|
||||||
@@ -15,7 +15,8 @@
|
|||||||
$: dynamicMapping = info.keyCode && $osLayout.get(info.keyCode);
|
$: dynamicMapping = info.keyCode && $osLayout.get(info.keyCode);
|
||||||
|
|
||||||
$: tooltip =
|
$: tooltip =
|
||||||
(info.title ?? info.id ?? `0x${info.code.toString(16)}`) +
|
`<${info.id ?? `0x${info.code.toString(16)}`}> ` +
|
||||||
|
(info.title ?? "") +
|
||||||
(info.variant === "left"
|
(info.variant === "left"
|
||||||
? " (left)"
|
? " (left)"
|
||||||
: info.variant === "right"
|
: info.variant === "right"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { KEYMAP_CODES } from "$lib/serial/keymap-codes";
|
import { KEYMAP_CODES } from "$lib/serial/keymap-codes";
|
||||||
import type { KeyInfo } from "$lib/serial/keymap-codes";
|
import type { KeyInfo } from "$lib/serial/keymap-codes";
|
||||||
import LL from "../../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
import Action from "$lib/components/Action.svelte";
|
import Action from "$lib/components/Action.svelte";
|
||||||
|
|
||||||
export let id: number | KeyInfo;
|
export let id: number | KeyInfo;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import FlexSearch from "flexsearch";
|
import FlexSearch from "flexsearch";
|
||||||
import { createEventDispatcher, onMount } from "svelte";
|
import { createEventDispatcher, onMount } from "svelte";
|
||||||
import ActionListItem from "$lib/components/ActionListItem.svelte";
|
import ActionListItem from "$lib/components/ActionListItem.svelte";
|
||||||
import LL from "../../../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
import { action } from "$lib/title";
|
import { action } from "$lib/title";
|
||||||
|
|
||||||
export let currentAction: number | undefined = undefined;
|
export let currentAction: number | undefined = undefined;
|
||||||
|
|||||||
@@ -127,12 +127,11 @@
|
|||||||
const clickedGroup = groupParent.children.item(index) as SVGGElement;
|
const clickedGroup = groupParent.children.item(index) as SVGGElement;
|
||||||
const nextAction = get(layout)[get(activeLayer)]?.[keyInfo.id];
|
const nextAction = get(layout)[get(activeLayer)]?.[keyInfo.id];
|
||||||
const currentAction = get(deviceLayout)[get(activeLayer)]?.[keyInfo.id];
|
const currentAction = get(deviceLayout)[get(activeLayer)]?.[keyInfo.id];
|
||||||
if (!nextAction || !currentAction) return;
|
|
||||||
const component = new ActionSelector({
|
const component = new ActionSelector({
|
||||||
target: document.body,
|
target: document.body,
|
||||||
props: {
|
props: {
|
||||||
currentAction,
|
currentAction,
|
||||||
nextAction: nextAction.isApplied ? undefined : nextAction.action,
|
nextAction: nextAction?.isApplied ? undefined : nextAction?.action,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const dialog = document.querySelector("dialog > div") as HTMLDivElement;
|
const dialog = document.querySelector("dialog > div") as HTMLDivElement;
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
import("$lib/assets/layouts/one.yml").then(
|
import("$lib/assets/layouts/one.yml").then(
|
||||||
(it) => it.default as VisualLayout,
|
(it) => it.default as VisualLayout,
|
||||||
),
|
),
|
||||||
|
TWO: () =>
|
||||||
|
import("$lib/assets/layouts/one.yml").then(
|
||||||
|
(it) => it.default as VisualLayout,
|
||||||
|
),
|
||||||
LITE: () =>
|
LITE: () =>
|
||||||
import("$lib/assets/layouts/lite.yml").then(
|
import("$lib/assets/layouts/lite.yml").then(
|
||||||
(it) => it.default as VisualLayout,
|
(it) => it.default as VisualLayout,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
} from "$lib/undo-redo";
|
} from "$lib/undo-redo";
|
||||||
import { ChangeType, chords } from "$lib/undo-redo";
|
import { ChangeType, chords } from "$lib/undo-redo";
|
||||||
import ActionString from "$lib/components/ActionString.svelte";
|
import ActionString from "$lib/components/ActionString.svelte";
|
||||||
import LL from "../../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
import { KEYMAP_IDS } from "$lib/serial/keymap-codes";
|
import { KEYMAP_IDS } from "$lib/serial/keymap-codes";
|
||||||
|
|
||||||
export let changes: Change[] = [
|
export let changes: Change[] = [
|
||||||
|
|||||||
@@ -55,3 +55,19 @@ export function deserializeActions(native: bigint): number[] {
|
|||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hashes a chord input the same way as CCOS
|
||||||
|
*/
|
||||||
|
export function hashChord(actions: number[]) {
|
||||||
|
const chord = new Uint8Array(16);
|
||||||
|
const view = new DataView(chord.buffer);
|
||||||
|
const serialized = serializeActions(actions);
|
||||||
|
view.setBigUint64(0, serialized & 0xffff_ffff_ffff_ffffn, true);
|
||||||
|
view.setBigUint64(8, serialized >> 64n, true);
|
||||||
|
let hash = 2166136261;
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
hash = Math.imul(hash ^ view.getUint8(i), 16777619);
|
||||||
|
}
|
||||||
|
return hash & 0x3fff_ffff;
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { browser } from "$app/environment";
|
|||||||
|
|
||||||
const PORT_FILTERS: Map<string, SerialPortFilter> = new Map([
|
const PORT_FILTERS: Map<string, SerialPortFilter> = new Map([
|
||||||
["ONE M0", { usbProductId: 32783, usbVendorId: 9114 }],
|
["ONE M0", { usbProductId: 32783, usbVendorId: 9114 }],
|
||||||
|
["TWO S3", { usbProductId: 0x0056, usbVendorId: 0x2886 }],
|
||||||
["LITE S2", { usbProductId: 33070, usbVendorId: 12346 }],
|
["LITE S2", { usbProductId: 33070, usbVendorId: 12346 }],
|
||||||
["LITE M0", { usbProductId: 32796, usbVendorId: 9114 }],
|
["LITE M0", { usbProductId: 32796, usbVendorId: 9114 }],
|
||||||
["X", { usbProductId: 33163, usbVendorId: 12346 }],
|
["X", { usbProductId: 33163, usbVendorId: 12346 }],
|
||||||
@@ -19,6 +20,7 @@ const PORT_FILTERS: Map<string, SerialPortFilter> = new Map([
|
|||||||
|
|
||||||
const KEY_COUNTS = {
|
const KEY_COUNTS = {
|
||||||
ONE: 90,
|
ONE: 90,
|
||||||
|
TWO: 90,
|
||||||
LITE: 67,
|
LITE: 67,
|
||||||
X: 256,
|
X: 256,
|
||||||
} as const;
|
} as const;
|
||||||
@@ -87,8 +89,8 @@ export class CharaDevice {
|
|||||||
|
|
||||||
version!: SemVer;
|
version!: SemVer;
|
||||||
company!: "CHARACHORDER";
|
company!: "CHARACHORDER";
|
||||||
device!: "ONE" | "LITE" | "X";
|
device!: "ONE" | "TWO" | "LITE" | "X";
|
||||||
chipset!: "M0" | "S2";
|
chipset!: "M0" | "S2" | "S3";
|
||||||
keyCount!: 90 | 67 | 256;
|
keyCount!: 90 | 67 | 256;
|
||||||
|
|
||||||
get portInfo() {
|
get portInfo() {
|
||||||
@@ -125,8 +127,8 @@ export class CharaDevice {
|
|||||||
);
|
);
|
||||||
const [company, device, chipset] = await this.send(3, "ID");
|
const [company, device, chipset] = await this.send(3, "ID");
|
||||||
this.company = company as "CHARACHORDER";
|
this.company = company as "CHARACHORDER";
|
||||||
this.device = device as "ONE" | "LITE" | "X";
|
this.device = device as "ONE" | "TWO" | "LITE" | "X";
|
||||||
this.chipset = chipset as "M0" | "S2";
|
this.chipset = chipset as "M0" | "S2" | "S3";
|
||||||
this.keyCount = KEY_COUNTS[this.device];
|
this.keyCount = KEY_COUNTS[this.device];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e);
|
alert(e);
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ export async function charaFileFromUriComponent<T extends CharaFiles>(
|
|||||||
.stream()
|
.stream()
|
||||||
.pipeThrough(new DecompressionStream("deflate"));
|
.pipeThrough(new DecompressionStream("deflate"));
|
||||||
const actions = new Uint8Array(await new Response(stream).arrayBuffer());
|
const actions = new Uint8Array(await new Response(stream).arrayBuffer());
|
||||||
console.log(actions);
|
|
||||||
file[key] = deserializeActionArray(actions);
|
file[key] = deserializeActionArray(actions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { persistentWritable } from "$lib/storage";
|
import { persistentWritable } from "$lib/storage";
|
||||||
import { derived } from "svelte/store";
|
import { derived } from "svelte/store";
|
||||||
import type { Chord } from "$lib/serial/chord";
|
import { hashChord, type Chord } from "$lib/serial/chord";
|
||||||
import {
|
import {
|
||||||
deviceChords,
|
deviceChords,
|
||||||
deviceLayout,
|
deviceLayout,
|
||||||
@@ -158,3 +158,9 @@ export const chords = derived([overlay, deviceChords], ([overlay, chords]) => {
|
|||||||
a.localeCompare(b),
|
a.localeCompare(b),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const chordHashes = derived(
|
||||||
|
chords,
|
||||||
|
(chords) =>
|
||||||
|
new Map(chords.map((chord) => [hashChord(chord.actions), chord] as const)),
|
||||||
|
);
|
||||||
|
|||||||
@@ -20,10 +20,10 @@
|
|||||||
import "tippy.js/dist/tippy.css";
|
import "tippy.js/dist/tippy.css";
|
||||||
import tippy from "tippy.js";
|
import tippy from "tippy.js";
|
||||||
import { theme, userPreferences } from "$lib/preferences.js";
|
import { theme, userPreferences } from "$lib/preferences.js";
|
||||||
import { LL, setLocale } from "../i18n/i18n-svelte";
|
import { LL, setLocale } from "$i18n/i18n-svelte";
|
||||||
import { loadLocale } from "../i18n/i18n-util.sync";
|
import { loadLocale } from "$i18n/i18n-util.sync";
|
||||||
import { detectLocale } from "../i18n/i18n-util";
|
import { detectLocale } from "$i18n/i18n-util";
|
||||||
import type { Locales } from "../i18n/i18n-types";
|
import type { Locales } from "$i18n/i18n-types";
|
||||||
import Footer from "./Footer.svelte";
|
import Footer from "./Footer.svelte";
|
||||||
import { osLayout, runLayoutDetection } from "$lib/os-layout.js";
|
import { osLayout, runLayoutDetection } from "$lib/os-layout.js";
|
||||||
import PageTransition from "./PageTransition.svelte";
|
import PageTransition from "./PageTransition.svelte";
|
||||||
13
src/routes/(app)/+layout.ts
Normal file
13
src/routes/(app)/+layout.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import type { LayoutLoad } from "./$types";
|
||||||
|
import { browser } from "$app/environment";
|
||||||
|
import { charaFileFromUriComponent } from "$lib/share/share-url";
|
||||||
|
|
||||||
|
export const load = (async ({ url, data, fetch }) => {
|
||||||
|
const importFile = browser && new URLSearchParams(url.search).get("import");
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
importFile: importFile
|
||||||
|
? await charaFileFromUriComponent(importFile, fetch)
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
}) satisfies LayoutLoad;
|
||||||
@@ -2,5 +2,5 @@ import { redirect } from "@sveltejs/kit";
|
|||||||
import type { PageLoad } from "./$types";
|
import type { PageLoad } from "./$types";
|
||||||
|
|
||||||
export const load = (() => {
|
export const load = (() => {
|
||||||
throw redirect(302, "/config/");
|
redirect(302, "/config/");
|
||||||
}) satisfies PageLoad;
|
}) satisfies PageLoad;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { preference } from "$lib/preferences";
|
import { preference } from "$lib/preferences";
|
||||||
import LL from "../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
import {
|
import {
|
||||||
createChordBackup,
|
createChordBackup,
|
||||||
createLayoutBackup,
|
createLayoutBackup,
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import LL from "../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<dialog open>
|
<dialog open>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { page } from "$app/stores";
|
import { page } from "$app/stores";
|
||||||
import LL from "../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
|
|
||||||
$: paths = [
|
$: paths = [
|
||||||
{
|
{
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
import { browser } from "$app/environment";
|
import { browser } from "$app/environment";
|
||||||
import { slide, fade } from "svelte/transition";
|
import { slide, fade } from "svelte/transition";
|
||||||
import { preference } from "$lib/preferences";
|
import { preference } from "$lib/preferences";
|
||||||
import LL from "../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
import { downloadBackup } from "$lib/backup/backup";
|
import { downloadBackup } from "$lib/backup/backup";
|
||||||
|
|
||||||
function reboot() {
|
function reboot() {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import LL from "../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
import {
|
import {
|
||||||
changes,
|
changes,
|
||||||
ChangeType,
|
ChangeType,
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { browser, version } from "$app/environment";
|
import { browser, version } from "$app/environment";
|
||||||
import { action } from "$lib/title";
|
import { action } from "$lib/title";
|
||||||
import LL, { setLocale } from "../i18n/i18n-svelte";
|
import LL, { setLocale } from "$i18n/i18n-svelte";
|
||||||
import { theme } from "$lib/preferences.js";
|
import { theme } from "$lib/preferences.js";
|
||||||
import type { Locales } from "../i18n/i18n-types";
|
import type { Locales } from "$i18n/i18n-types";
|
||||||
import { detectLocale, locales } from "../i18n/i18n-util";
|
import { detectLocale, locales } from "$i18n/i18n-util";
|
||||||
import { loadLocaleAsync } from "../i18n/i18n-util.async";
|
import { loadLocaleAsync } from "$i18n/i18n-util.async";
|
||||||
import { tick } from "svelte";
|
import { tick } from "svelte";
|
||||||
import SyncOverlay from "./SyncOverlay.svelte";
|
import SyncOverlay from "./SyncOverlay.svelte";
|
||||||
import { serialPort } from "$lib/serial/connection";
|
import { serialPort } from "$lib/serial/connection";
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
import { browser } from "$app/environment";
|
import { browser } from "$app/environment";
|
||||||
import { userPreferences } from "$lib/preferences";
|
import { userPreferences } from "$lib/preferences";
|
||||||
import { action } from "$lib/title";
|
import { action } from "$lib/title";
|
||||||
import LL from "../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
import ConfigTabs from "./ConfigTabs.svelte";
|
import ConfigTabs from "./ConfigTabs.svelte";
|
||||||
import EditActions from "./EditActions.svelte";
|
import EditActions from "./EditActions.svelte";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
syncStatus,
|
syncStatus,
|
||||||
sync,
|
sync,
|
||||||
} from "$lib/serial/connection";
|
} from "$lib/serial/connection";
|
||||||
import LL from "../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
import { slide } from "svelte/transition";
|
import { slide } from "svelte/transition";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -2,5 +2,5 @@ import { redirect } from "@sveltejs/kit";
|
|||||||
import type { PageLoad } from "./$types";
|
import type { PageLoad } from "./$types";
|
||||||
|
|
||||||
export const load = (() => {
|
export const load = (() => {
|
||||||
throw redirect(302, "/config/layout/");
|
redirect(302, "/config/layout/");
|
||||||
}) satisfies PageLoad;
|
}) satisfies PageLoad;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import LL from "../../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{$LL.share.URL_COPIED()}
|
{$LL.share.URL_COPIED()}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { KEYMAP_CODES } from "$lib/serial/keymap-codes";
|
import { KEYMAP_CODES } from "$lib/serial/keymap-codes";
|
||||||
import FlexSearch from "flexsearch";
|
import FlexSearch from "flexsearch";
|
||||||
import LL from "../../../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
import { action } from "$lib/title";
|
import { action } from "$lib/title";
|
||||||
import { onDestroy, onMount, setContext, tick } from "svelte";
|
import { onDestroy, onMount, setContext, tick } from "svelte";
|
||||||
import { changes, ChangeType, chords } from "$lib/undo-redo";
|
import { changes, ChangeType, chords } from "$lib/undo-redo";
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
resizeObserver?.disconnect();
|
resizeObserver?.disconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
let index = new FlexSearch.Index({ tokenize: "full" });
|
let index = new FlexSearch.Index();
|
||||||
let searchIndex = writable<FlexSearch.Index | undefined>(undefined);
|
let searchIndex = writable<FlexSearch.Index | undefined>(undefined);
|
||||||
$: {
|
$: {
|
||||||
abortIndexing?.();
|
abortIndexing?.();
|
||||||
@@ -43,22 +43,72 @@
|
|||||||
buildIndex($chords, $osLayout).then(searchIndex.set);
|
buildIndex($chords, $osLayout).then(searchIndex.set);
|
||||||
}
|
}
|
||||||
|
|
||||||
function plainPhrase(phrase: number[], osLayout: Map<string, string>) {
|
function encodeChord(chord: ChordInfo, osLayout: Map<string, string>) {
|
||||||
return phrase
|
const plainPhrase: string[] = [""];
|
||||||
|
const extraActions: string[] = [];
|
||||||
|
const extraCodes: string[] = [];
|
||||||
|
|
||||||
|
for (const actionCode of chord.phrase ?? []) {
|
||||||
|
const action = KEYMAP_CODES.get(actionCode);
|
||||||
|
if (!action) {
|
||||||
|
extraCodes.push(`0x${actionCode.toString(16)}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const osCode = action.keyCode && osLayout.get(action.keyCode);
|
||||||
|
const token = osCode?.length === 1 ? osCode : action.display || action.id;
|
||||||
|
if (!token) {
|
||||||
|
extraCodes.push(`0x${action.code.toString(16)}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^\s$/.test(token) && plainPhrase.at(-1) !== "") {
|
||||||
|
plainPhrase.push("");
|
||||||
|
} else if (token.length === 1) {
|
||||||
|
plainPhrase[plainPhrase.length - 1] =
|
||||||
|
plainPhrase[plainPhrase.length - 1] + token;
|
||||||
|
} else {
|
||||||
|
extraActions.push(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chord.phrase?.[0] === 298) {
|
||||||
|
plainPhrase.push("suffix");
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
["ARROW_LT", "ARROW_RT", "ARROW_UP", "ARROW_DN"].some((it) =>
|
||||||
|
extraActions.includes(it),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
plainPhrase.push("cursor warp");
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
["CTRL", "ALT", "GUI", "ENTER", "TAB"].some((it) =>
|
||||||
|
extraActions.includes(it),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
plainPhrase.push("macro");
|
||||||
|
}
|
||||||
|
if (chord.actions[0] !== 0) {
|
||||||
|
plainPhrase.push("compound");
|
||||||
|
}
|
||||||
|
|
||||||
|
const input = chord.actions
|
||||||
|
.slice(chord.actions.lastIndexOf(0) + 1)
|
||||||
.map((it) => {
|
.map((it) => {
|
||||||
const info = KEYMAP_CODES.get(it);
|
const info = KEYMAP_CODES.get(it);
|
||||||
if (!info) return "";
|
if (!info) return `0x${it.toString(16)}`;
|
||||||
|
const osCode = info.keyCode && osLayout.get(info.keyCode);
|
||||||
|
const result = osCode?.length === 1 ? osCode : info.id;
|
||||||
|
return result ?? `0x${it.toString(16)}`;
|
||||||
|
});
|
||||||
|
|
||||||
const bestGuess =
|
return [
|
||||||
(info.keyCode && osLayout.get(info.keyCode)) ||
|
...plainPhrase,
|
||||||
info.display ||
|
`+${input.join("+")}`,
|
||||||
info.id ||
|
...new Set(extraActions),
|
||||||
"";
|
...new Set(extraCodes),
|
||||||
|
].join(" ");
|
||||||
return bestGuess.length === 1 ? bestGuess : "";
|
|
||||||
})
|
|
||||||
.filter((it) => !!it)
|
|
||||||
.join("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildIndex(
|
async function buildIndex(
|
||||||
@@ -66,7 +116,23 @@
|
|||||||
osLayout: Map<string, string>,
|
osLayout: Map<string, string>,
|
||||||
): Promise<FlexSearch.Index> {
|
): Promise<FlexSearch.Index> {
|
||||||
if (chords.length === 0 || !browser) return index;
|
if (chords.length === 0 || !browser) return index;
|
||||||
index = new FlexSearch.Index({ tokenize: "full" });
|
index = new FlexSearch.Index({
|
||||||
|
tokenize: "full",
|
||||||
|
encode(phrase: string) {
|
||||||
|
return phrase.split(/\s+/).flatMap((it) => {
|
||||||
|
if (/^[A-Z_]+$/.test(it)) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
if (it.startsWith("+")) {
|
||||||
|
return it
|
||||||
|
.slice(1)
|
||||||
|
.split("+")
|
||||||
|
.map((it) => `+${it}`);
|
||||||
|
}
|
||||||
|
return it.toLowerCase();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
let abort = false;
|
let abort = false;
|
||||||
abortIndexing = () => {
|
abortIndexing = () => {
|
||||||
abort = true;
|
abort = true;
|
||||||
@@ -78,7 +144,7 @@
|
|||||||
progress = i;
|
progress = i;
|
||||||
|
|
||||||
if ("phrase" in chord) {
|
if ("phrase" in chord) {
|
||||||
await index.addAsync(i, plainPhrase(chord.phrase, osLayout));
|
await index.addAsync(i, encodeChord(chord, osLayout));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return index;
|
return index;
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ChordInfo } from "$lib/undo-redo";
|
import type { ChordInfo } from "$lib/undo-redo";
|
||||||
import { changes, ChangeType } from "$lib/undo-redo";
|
import { changes, chordHashes, ChangeType } from "$lib/undo-redo";
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
import LL from "../../../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
import ActionString from "$lib/components/ActionString.svelte";
|
import ActionString from "$lib/components/ActionString.svelte";
|
||||||
import { selectAction } from "./action-selector";
|
import { selectAction } from "./action-selector";
|
||||||
import { serialPort } from "$lib/serial/connection";
|
import { serialPort } from "$lib/serial/connection";
|
||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
import { inputToAction } from "./input-converter";
|
import { inputToAction } from "./input-converter";
|
||||||
|
import { hashChord } from "$lib/serial/chord";
|
||||||
|
|
||||||
export let chord: ChordInfo | undefined = undefined;
|
export let chord: ChordInfo | undefined = undefined;
|
||||||
|
|
||||||
@@ -21,14 +22,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function makeChordInput(...actions: number[]) {
|
function makeChordInput(...actions: number[]) {
|
||||||
const compound = compoundIndices ?? [];
|
const compound = compoundInputs[0]
|
||||||
|
? hashChord(compoundInputs[0].actions)
|
||||||
|
: 0;
|
||||||
return [
|
return [
|
||||||
...compound,
|
|
||||||
...Array.from(
|
...Array.from(
|
||||||
{
|
{
|
||||||
length: 12 - (compound.length + actions.length + 1),
|
length: 12 - actions.length,
|
||||||
},
|
},
|
||||||
() => 0,
|
(_, i) => (compound >> (i * 10)) & 0x3ff,
|
||||||
),
|
),
|
||||||
...actions.toSorted(compare),
|
...actions.toSorted(compare),
|
||||||
];
|
];
|
||||||
@@ -73,7 +75,6 @@
|
|||||||
function addSpecial(event: MouseEvent) {
|
function addSpecial(event: MouseEvent) {
|
||||||
selectAction(event, (action) => {
|
selectAction(event, (action) => {
|
||||||
changes.update((changes) => {
|
changes.update((changes) => {
|
||||||
console.log(compoundIndices, chordActions, action);
|
|
||||||
changes.push({
|
changes.push({
|
||||||
type: ChangeType.Chord,
|
type: ChangeType.Chord,
|
||||||
id: chord!.id,
|
id: chord!.id,
|
||||||
@@ -85,10 +86,30 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function* resolveCompound(chord?: ChordInfo) {
|
||||||
|
if (!chord) return;
|
||||||
|
let current = chord;
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
if (current.actions[3] !== 0) return;
|
||||||
|
const compound = current.actions
|
||||||
|
.slice(0, 3)
|
||||||
|
.reduce((a, b, i) => a | (b << (i * 10)));
|
||||||
|
if (compound === 0) return;
|
||||||
|
const next = $chordHashes.get(compound);
|
||||||
|
if (!next) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = next;
|
||||||
|
yield next;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$: chordActions = chord?.actions
|
$: chordActions = chord?.actions
|
||||||
.slice(chord.actions.lastIndexOf(0) + 1)
|
.slice(chord.actions.lastIndexOf(0) + 1)
|
||||||
.toSorted(compare);
|
.toSorted(compare);
|
||||||
$: compoundIndices = chord?.actions.slice(0, chord.actions.indexOf(0));
|
$: compoundInputs = [...resolveCompound(chord)].reverse();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@@ -110,12 +131,15 @@
|
|||||||
<span>{$LL.configure.chords.NEW_CHORD()}</span>
|
<span>{$LL.configure.chords.NEW_CHORD()}</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if !editing}
|
{#if !editing}
|
||||||
{#each compoundIndices ?? [] as index}
|
{#each compoundInputs as compound}
|
||||||
<sub>{index}</sub>
|
<sub
|
||||||
{/each}
|
><ActionString
|
||||||
{#if compoundIndices?.length}
|
display="keys"
|
||||||
|
actions={compound.actions.slice(compound.actions.lastIndexOf(0) + 1)}
|
||||||
|
></ActionString>
|
||||||
|
</sub>
|
||||||
<span>→</span>
|
<span>→</span>
|
||||||
{/if}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
<ActionString
|
<ActionString
|
||||||
display="keys"
|
display="keys"
|
||||||
@@ -7,11 +7,16 @@
|
|||||||
import { keymap } from "@codemirror/view";
|
import { keymap } from "@codemirror/view";
|
||||||
import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
||||||
import { tags } from "@lezer/highlight";
|
import { tags } from "@lezer/highlight";
|
||||||
import LL from "../../i18n/i18n-svelte";
|
import LL from "$i18n/i18n-svelte";
|
||||||
import type { CompletionContext } from "@codemirror/autocomplete";
|
import type { CompletionContext, Completion } from "@codemirror/autocomplete";
|
||||||
|
import { syntaxTree } from "@codemirror/language";
|
||||||
import { serialPort } from "$lib/serial/connection";
|
import { serialPort } from "$lib/serial/connection";
|
||||||
import type { CharaDevice } from "$lib/serial/device";
|
|
||||||
import examplePlugin from "./example-plugin.js?raw";
|
import examplePlugin from "./example-plugin.js?raw";
|
||||||
|
import {
|
||||||
|
charaMethods,
|
||||||
|
type ChannelCharaEventData,
|
||||||
|
type ChannelResponseEventData,
|
||||||
|
} from "./plugin-types";
|
||||||
|
|
||||||
let theme = EditorView.baseTheme({
|
let theme = EditorView.baseTheme({
|
||||||
".cm-editor .cm-content": {
|
".cm-editor .cm-content": {
|
||||||
@@ -40,6 +45,18 @@
|
|||||||
background: "transparent !important",
|
background: "transparent !important",
|
||||||
backdropFilter: "invert(0.3)",
|
backdropFilter: "invert(0.3)",
|
||||||
},
|
},
|
||||||
|
".cm-tooltip": {
|
||||||
|
backgroundColor: "var(--md-sys-color-background) !important",
|
||||||
|
color: "var(--md-sys-color-on-background)",
|
||||||
|
borderColor: "var(--md-sys-color-outline)",
|
||||||
|
},
|
||||||
|
".cm-tooltip-autocomplete ul li[aria-selected]": {
|
||||||
|
backgroundColor: "var(--md-sys-color-primary) !important",
|
||||||
|
color: "var(--md-sys-color-on-primary) !important",
|
||||||
|
},
|
||||||
|
".cm-completionIcon.cm-completionIcon-keyword::after": {
|
||||||
|
content: "'🗝'",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const highlightStyle = HighlightStyle.define(
|
const highlightStyle = HighlightStyle.define(
|
||||||
[
|
[
|
||||||
@@ -56,11 +73,71 @@
|
|||||||
all: { fontFamily: '"Noto Sans Mono", monospace', fontSize: "14px" },
|
all: { fontFamily: '"Noto Sans Mono", monospace', fontSize: "14px" },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const globalsCompletion: Completion[] = [
|
||||||
|
{ label: "Chara", type: "class", boost: 90 },
|
||||||
|
{ label: "Actions", type: "class", boost: 90 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const actionsCompletion: Completion[] = Array.from(
|
||||||
|
KEYMAP_CODES,
|
||||||
|
([id, info]) => {
|
||||||
|
const isValidIdentifier =
|
||||||
|
info.id && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(info.id);
|
||||||
|
return {
|
||||||
|
label: info.id
|
||||||
|
? isValidIdentifier
|
||||||
|
? info.id
|
||||||
|
: `["${info.id}"]`
|
||||||
|
: info.id!,
|
||||||
|
displayLabel: info.id,
|
||||||
|
detail: [info.title, `(0x${id.toString(16)})`, info.description]
|
||||||
|
.filter((it) => !!it)
|
||||||
|
.join(" "),
|
||||||
|
section: info.category,
|
||||||
|
boost: isValidIdentifier ? Math.min(info.id?.length ?? 0, 10) + 50 : 40,
|
||||||
|
type: "property",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
).filter((it) => it.label !== undefined);
|
||||||
|
|
||||||
const completion = javascriptLanguage.data.of({
|
const completion = javascriptLanguage.data.of({
|
||||||
autocomplete: function completeGlobals(context: CompletionContext) {
|
autocomplete: function completeGlobals(context: CompletionContext) {
|
||||||
if (context.matchBefore(/Chara\./)) {
|
let nodeBefore = syntaxTree(context.state).resolveInner(context.pos, -1);
|
||||||
// TODO
|
if (nodeBefore.name === "VariableName") {
|
||||||
|
return {
|
||||||
|
from: nodeBefore.from,
|
||||||
|
options: globalsCompletion,
|
||||||
|
};
|
||||||
|
} else if (nodeBefore.name === "Script") {
|
||||||
|
return {
|
||||||
|
from: context.pos,
|
||||||
|
options: globalsCompletion,
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
(nodeBefore.name === "PropertyName" || nodeBefore.name === ".") &&
|
||||||
|
nodeBefore.parent?.name === "MemberExpression" &&
|
||||||
|
nodeBefore.parent.firstChild
|
||||||
|
) {
|
||||||
|
const variable = nodeBefore.parent.firstChild;
|
||||||
|
const variableName = context.state.sliceDoc(variable.from, variable.to);
|
||||||
|
if (variableName === "Actions") {
|
||||||
|
return {
|
||||||
|
from:
|
||||||
|
nodeBefore.name === "PropertyName"
|
||||||
|
? nodeBefore.from
|
||||||
|
: nodeBefore.to,
|
||||||
|
options: actionsCompletion,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let parent = nodeBefore.prevSibling;
|
||||||
|
while (parent !== null && parent?.name !== "VariableName") {
|
||||||
|
parent = parent.prevSibling;
|
||||||
|
}
|
||||||
|
if (parent) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -78,22 +155,6 @@
|
|||||||
doc: examplePlugin,
|
doc: examplePlugin,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const charaMethods = [
|
|
||||||
"reboot",
|
|
||||||
"bootloader",
|
|
||||||
"getRamBytesAvailable",
|
|
||||||
"getSetting",
|
|
||||||
"setSetting",
|
|
||||||
"getLayoutKey",
|
|
||||||
"setLayoutKey",
|
|
||||||
"deleteChord",
|
|
||||||
"setChord",
|
|
||||||
"getChordPhrase",
|
|
||||||
"getChordCount",
|
|
||||||
"getChord",
|
|
||||||
"send",
|
|
||||||
] satisfies Array<keyof CharaDevice>;
|
|
||||||
$: channels = $serialPort
|
$: channels = $serialPort
|
||||||
? ({
|
? ({
|
||||||
getVersion: async (..._args: unknown[]) => $serialPort.version,
|
getVersion: async (..._args: unknown[]) => $serialPort.version,
|
||||||
@@ -122,7 +183,10 @@
|
|||||||
|
|
||||||
const [channel, params] = event.data;
|
const [channel, params] = event.data;
|
||||||
const response = channels[channel as keyof typeof channels](...params);
|
const response = channels[channel as keyof typeof channels](...params);
|
||||||
frame.contentWindow!.postMessage({ response: await response }, "*");
|
frame.contentWindow!.postMessage(
|
||||||
|
{ response: await response } satisfies ChannelResponseEventData,
|
||||||
|
"*",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function runPlugin() {
|
function runPlugin() {
|
||||||
@@ -131,7 +195,7 @@
|
|||||||
actionCodes: KEYMAP_CODES,
|
actionCodes: KEYMAP_CODES,
|
||||||
script: editorView.state.doc.toString(),
|
script: editorView.state.doc.toString(),
|
||||||
charaChannels: Object.keys(channels),
|
charaChannels: Object.keys(channels),
|
||||||
},
|
} satisfies ChannelCharaEventData,
|
||||||
"*",
|
"*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
30
src/routes/(app)/plugin/plugin-types.ts
Normal file
30
src/routes/(app)/plugin/plugin-types.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import type { CharaDevice } from "$lib/serial/device";
|
||||||
|
import type { KeyInfo } from "$lib/serial/keymap-codes";
|
||||||
|
|
||||||
|
export const charaMethods = [
|
||||||
|
"reboot",
|
||||||
|
"bootloader",
|
||||||
|
"getRamBytesAvailable",
|
||||||
|
"getSetting",
|
||||||
|
"setSetting",
|
||||||
|
"getLayoutKey",
|
||||||
|
"setLayoutKey",
|
||||||
|
"deleteChord",
|
||||||
|
"setChord",
|
||||||
|
"getChordPhrase",
|
||||||
|
"getChordCount",
|
||||||
|
"getChord",
|
||||||
|
"send",
|
||||||
|
] as const satisfies Array<keyof CharaDevice>;
|
||||||
|
|
||||||
|
export interface ChannelResponseEventData {
|
||||||
|
response: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChannelCharaEventData {
|
||||||
|
charaChannels: string[];
|
||||||
|
script: string;
|
||||||
|
actionCodes: Map<number, KeyInfo>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ChannelEventData = ChannelResponseEventData | ChannelCharaEventData;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { LL } from "../../i18n/i18n-svelte";
|
import { LL } from "$i18n/i18n-svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h1>{$LL.update.TITLE()}</h1>
|
<h1>{$LL.update.TITLE()}</h1>
|
||||||
@@ -1,16 +1,2 @@
|
|||||||
import type { LayoutLoad } from "./$types";
|
|
||||||
import { browser } from "$app/environment";
|
|
||||||
import { charaFileFromUriComponent } from "$lib/share/share-url";
|
|
||||||
|
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
export const trailingSlash = "always";
|
export const trailingSlash = "always";
|
||||||
|
|
||||||
export const load = (async ({ url, data, fetch }) => {
|
|
||||||
const importFile = browser && new URLSearchParams(url.search).get("import");
|
|
||||||
return {
|
|
||||||
...data,
|
|
||||||
importFile: importFile
|
|
||||||
? await charaFileFromUriComponent(importFile, fetch)
|
|
||||||
: undefined,
|
|
||||||
};
|
|
||||||
}) satisfies LayoutLoad;
|
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
<script>
|
<script lang="ts">
|
||||||
// @ts-nocheck
|
import type { ChannelEventData } from "../(app)/plugin/plugin-types";
|
||||||
let ongoingRequest;
|
|
||||||
let resolveRequest;
|
let ongoingRequest: Promise<unknown> | undefined = undefined;
|
||||||
let source;
|
let resolveRequest: ((data: unknown) => void) | undefined = undefined;
|
||||||
async function post(channel, args) {
|
let source: MessageEventSource | undefined = undefined;
|
||||||
|
|
||||||
|
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
|
||||||
|
|
||||||
|
async function post(channel: string, args: unknown[]) {
|
||||||
while (ongoingRequest) {
|
while (ongoingRequest) {
|
||||||
await ongoingRequest;
|
await ongoingRequest;
|
||||||
}
|
}
|
||||||
ongoingRequest = new Promise((resolve) => {
|
ongoingRequest = new Promise((resolve) => {
|
||||||
resolveRequest = resolve;
|
resolveRequest = resolve;
|
||||||
source.postMessage([channel, args], "*");
|
source?.postMessage([channel, args], { targetOrigin: "*" });
|
||||||
});
|
});
|
||||||
ongoingRequest.then(() => {
|
ongoingRequest.then(() => {
|
||||||
ongoingRequest = undefined;
|
ongoingRequest = undefined;
|
||||||
@@ -17,13 +21,13 @@
|
|||||||
return ongoingRequest;
|
return ongoingRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("message", (event) => {
|
function onMessage(event: MessageEvent<ChannelEventData>) {
|
||||||
if ("response" in event.data) {
|
if ("response" in event.data) {
|
||||||
resolveRequest(event.data.response);
|
resolveRequest?.(event.data.response);
|
||||||
} else {
|
} else {
|
||||||
source = event.source;
|
source = event.source ?? undefined;
|
||||||
|
|
||||||
var Action = event.data.actionCodes;
|
const Action = event.data.actionCodes;
|
||||||
Object.assign(
|
Object.assign(
|
||||||
Action,
|
Action,
|
||||||
Object.fromEntries(
|
Object.fromEntries(
|
||||||
@@ -33,12 +37,20 @@
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
var Chara = {};
|
const Chara = Object.fromEntries(
|
||||||
for (const fn of event.data.charaChannels) {
|
event.data.charaChannels.map((name) => [
|
||||||
Chara[fn] = (...args) => post(fn, args);
|
name,
|
||||||
}
|
(...args: unknown[]) => post(name, args),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
eval(`(async function(){${event.data.script}})()`);
|
AsyncFunction(
|
||||||
|
"Action",
|
||||||
|
"Chara",
|
||||||
|
'"use strict"\n' + event.data.script,
|
||||||
|
)(Action, Chara);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:message={onMessage} />
|
||||||
|
|||||||
60
static/sandbox/index.html
Normal file
60
static/sandbox/index.html
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<script>
|
||||||
|
/** @type {Promise<unknown> | undefined} */
|
||||||
|
let ongoingRequest = undefined;
|
||||||
|
/** @type {(data: unknown) => void | undefined} */
|
||||||
|
let resolveRequest = undefined;
|
||||||
|
/** @type {MessageEventSource | undefined} */
|
||||||
|
let source = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} channel
|
||||||
|
* @param {unknown} args
|
||||||
|
* @returns {Promise<unknown>}
|
||||||
|
*/
|
||||||
|
async function post(channel, args) {
|
||||||
|
while (ongoingRequest) {
|
||||||
|
await ongoingRequest;
|
||||||
|
}
|
||||||
|
ongoingRequest = new Promise((resolve) => {
|
||||||
|
resolveRequest = resolve;
|
||||||
|
source?.postMessage([channel, args], { targetOrigin: "*" });
|
||||||
|
});
|
||||||
|
ongoingRequest.then(() => {
|
||||||
|
ongoingRequest = undefined;
|
||||||
|
});
|
||||||
|
return ongoingRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {MessageEvent<import('../../src/routes/plugin/plugin-types').ChannelEventData>} event
|
||||||
|
*/
|
||||||
|
function onMessage(event) {
|
||||||
|
if ("response" in event.data) {
|
||||||
|
resolveRequest?.(event.data.response);
|
||||||
|
} else {
|
||||||
|
source = event.source ?? undefined;
|
||||||
|
|
||||||
|
const Action = event.data.actionCodes;
|
||||||
|
Object.assign(
|
||||||
|
Action,
|
||||||
|
Object.fromEntries(
|
||||||
|
Object.values(event.data.actionCodes)
|
||||||
|
.filter((it) => !!it.id)
|
||||||
|
.map((it) => [it.id, it]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
new Function("Action", "Chara", event.data.script)(
|
||||||
|
Action,
|
||||||
|
Object.fromEntries(
|
||||||
|
event.data.charaChannels.map((name) => [
|
||||||
|
name,
|
||||||
|
(...args) => post(name, args),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("message", onMessage);
|
||||||
|
</script>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import adapter from "@sveltejs/adapter-static";
|
import adapter from "@sveltejs/adapter-static";
|
||||||
import preprocess from "svelte-preprocess";
|
import { sveltePreprocess } from "svelte-preprocess";
|
||||||
import autoprefixer from "autoprefixer";
|
import autoprefixer from "autoprefixer";
|
||||||
import { readFile } from "fs/promises";
|
import { readFile } from "fs/promises";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
@@ -13,9 +13,12 @@ const { version } = JSON.parse(
|
|||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
const config = {
|
const config = {
|
||||||
preprocess: [preprocess({ postcss: { plugins: autoprefixer() } })],
|
preprocess: [sveltePreprocess({ postcss: { plugins: autoprefixer() } })],
|
||||||
kit: {
|
kit: {
|
||||||
adapter: adapter({ fallback: "404.html" }),
|
adapter: adapter({ fallback: "404.html" }),
|
||||||
|
alias: {
|
||||||
|
$i18n: "./src/i18n",
|
||||||
|
},
|
||||||
version: {
|
version: {
|
||||||
name: version,
|
name: version,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ process.env["VITE_HOMEPAGE_URL"] = repository.url.replace(/\.git$/, "");
|
|||||||
process.env["VITE_DOCS_URL"] = homepage;
|
process.env["VITE_DOCS_URL"] = homepage;
|
||||||
process.env["VITE_BUGS_URL"] = bugs.url;
|
process.env["VITE_BUGS_URL"] = bugs.url;
|
||||||
process.env["VITE_LEARN_URL"] = "https://www.iq-eq.io/";
|
process.env["VITE_LEARN_URL"] = "https://www.iq-eq.io/";
|
||||||
process.env["VITE_LATEST_FIRMWARE"] = "1.1.3";
|
process.env["VITE_LATEST_FIRMWARE"] = "1.1.4";
|
||||||
process.env["VITE_STORE_URL"] = "https://www.charachorder.com/";
|
process.env["VITE_STORE_URL"] = "https://www.charachorder.com/";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@@ -49,9 +49,10 @@ export default defineConfig({
|
|||||||
workbox: {
|
workbox: {
|
||||||
// https://vite-pwa-org.netlify.app/frameworks/sveltekit.html#globpatterns
|
// https://vite-pwa-org.netlify.app/frameworks/sveltekit.html#globpatterns
|
||||||
globPatterns: [
|
globPatterns: [
|
||||||
"client/**/*.{js,map,css,woff2,csv,png,svg}",
|
"client/**/*.{js,map,css,ico,woff2,csv,png,webp,svg,webmanifest}",
|
||||||
"prerendered/**/*.html",
|
"prerendered/**/*.html",
|
||||||
],
|
],
|
||||||
|
ignoreURLParametersMatching: [/^import$/],
|
||||||
},
|
},
|
||||||
manifest: {
|
manifest: {
|
||||||
name: "CharaChorder Device Manager",
|
name: "CharaChorder Device Manager",
|
||||||
|
|||||||
Reference in New Issue
Block a user