entangled-worlds-orbiters-shared 4.2.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,139 +0,0 @@
1
- // What the actions and sections are called in each language the platform speaks, beside the
2
- // English in the catalogues. One table, read by every app and the settings page, so a name is
3
- // translated once.
4
-
5
- import type { ShortcutAction } from './match.js'
6
-
7
- type Names = Readonly<Record<string, string>>
8
-
9
- const ES: Names = Object.freeze({
10
- "ew.player.playPause": "Reproducir o pausar",
11
- "ew.studio.commands": "Comandos",
12
- "ew.studio.copy": "Copiar la selección",
13
- "ew.studio.duplicate": "Duplicar la selección",
14
- "ew.studio.help": "Atajos de teclado",
15
- "ew.studio.paste": "Pegar",
16
- "ew.studio.playPause": "Reproducir o pausar",
17
- "ew.studio.redo": "Rehacer",
18
- "ew.studio.remove": "Quitar la selección",
19
- "ew.studio.step.animations": "Paso de animaciones",
20
- "ew.studio.step.audio": "Paso de audio",
21
- "ew.studio.step.release": "Paso de publicación",
22
- "ew.studio.step.third": "Paso de exportación (Publicación donde no hay exportación)",
23
- "ew.studio.undo": "Deshacer",
24
- "orbiters.play.body.moon": "Luna",
25
- "orbiters.play.body.star": "Estrella",
26
- "orbiters.play.body.world": "Mundo",
27
- "orbiters.play.dimension.1": "Primera dimensión",
28
- "orbiters.play.dimension.2": "Segunda dimensión",
29
- "orbiters.play.dimension.3": "Tercera dimensión",
30
- "orbiters.play.help": "Atajos de teclado",
31
- "orbiters.play.history.redo": "Rehacer",
32
- "orbiters.play.history.undo": "Deshacer",
33
- "orbiters.play.midiLearn": "MIDI learn",
34
- "orbiters.play.panel.cosmicLfo": "Cosmic LFO",
35
- "orbiters.play.panel.jamming": "Jamming",
36
- "orbiters.play.panel.playback": "Reproducción",
37
- "orbiters.play.panel.sensors": "Sensores",
38
- "orbiters.play.performanceMode": "Modo rendimiento",
39
- "orbiters.play.rec.hold": "Grabar mientras se mantiene",
40
- "orbiters.play.rec.toggle": "Activar o desactivar Rec",
41
- "orbiters.play.snapshot.recall.0": "Recuperar instantánea 0",
42
- "orbiters.play.snapshot.recall.1": "Recuperar instantánea 1",
43
- "orbiters.play.snapshot.recall.2": "Recuperar instantánea 2",
44
- "orbiters.play.snapshot.recall.3": "Recuperar instantánea 3",
45
- "orbiters.play.snapshot.recall.4": "Recuperar instantánea 4",
46
- "orbiters.play.snapshot.recall.5": "Recuperar instantánea 5",
47
- "orbiters.play.snapshot.recall.6": "Recuperar instantánea 6",
48
- "orbiters.play.snapshot.recall.7": "Recuperar instantánea 7",
49
- "orbiters.play.snapshot.recall.8": "Recuperar instantánea 8",
50
- "orbiters.play.snapshot.recall.9": "Recuperar instantánea 9",
51
- "orbiters.play.snapshot.store.0": "Guardar instantánea 0",
52
- "orbiters.play.snapshot.store.1": "Guardar instantánea 1",
53
- "orbiters.play.snapshot.store.2": "Guardar instantánea 2",
54
- "orbiters.play.snapshot.store.3": "Guardar instantánea 3",
55
- "orbiters.play.snapshot.store.4": "Guardar instantánea 4",
56
- "orbiters.play.snapshot.store.5": "Guardar instantánea 5",
57
- "orbiters.play.snapshot.store.6": "Guardar instantánea 6",
58
- "orbiters.play.snapshot.store.7": "Guardar instantánea 7",
59
- "orbiters.play.snapshot.store.8": "Guardar instantánea 8",
60
- "orbiters.play.snapshot.store.9": "Guardar instantánea 9",
61
- "orbiters.play.transport.restart": "Reproducir desde el principio",
62
- "orbiters.play.transport.toggle": "Reproducir o pausar",
63
- "orbiters.studio.map": "Mapear",
64
- "orbiters.studio.redo": "Rehacer",
65
- "orbiters.studio.undo": "Deshacer",
66
- "site.commands": "Comandos",
67
- "site.search": "Buscar",
68
- "site.go.home": "Inicio",
69
- "site.go.playground": "Playground",
70
- "site.go.notifications": "Notificaciones",
71
- "site.go.audios": "Mis audios",
72
- "site.go.orbiters": "Mis Orbiters",
73
- "site.go.worlds": "Mis Entangled Worlds",
74
- "site.go.collections": "Mis colecciones",
75
- "site.go.stars": "Mis estrellas",
76
- "site.new.orbiter": "Nuevo Orbiter",
77
- "site.new.audio": "Nuevo audio",
78
- "site.new.world": "Nuevo Entangled World",
79
- "site.settings": "Abrir la configuración",
80
- "section:site": "Plantasia Space",
81
- "app:plantasia-space": "Plantasia Space",
82
- "app:orbiters": "Orbiters",
83
- "app:entangled-worlds": "Entangled Worlds",
84
- "part:orbiters-play": "Tocar",
85
- "part:orbiters-studio": "Estudio",
86
- "part:ew-studio": "Estudio",
87
- "part:ew-player": "Reproductor",
88
- "section:orbiters-play": "Orbiters: tocar",
89
- "section:orbiters-studio": "Orbiter Studio",
90
- "section:ew-studio": "Estudio de Entangled Worlds",
91
- "section:ew-player": "Reproductor de Entangled Worlds",
92
- "orbiters.play.screenCapture": "Captura de pantalla",
93
- "orbiters.play.share": "Compartir",
94
- "orbiters.play.fullscreen": "Pantalla completa",
95
- "orbiters.play.commands": "Comandos",
96
- "group:edit": "Edición",
97
- "group:playback": "Reproducción",
98
- "group:steps": "Pasos",
99
- "group:help": "Ayuda",
100
- "group:transport": "Transporte",
101
- "group:view": "Vista",
102
- "group:snapshots": "Instantáneas",
103
- "group:panels": "Paneles",
104
- "group:orbiter": "Orbiter",
105
- "group:studio": "Estudio",
106
- "row:playPause": "Reproducir / pausar",
107
- "row:playFromStart": "Reproducir desde el inicio",
108
- "row:recHeld": "REC mientras se mantiene",
109
- "row:recLatch": "REC fijo",
110
- "row:undo": "Deshacer",
111
- "row:redo": "Rehacer",
112
- "row:dimensions": "Dimensión I · II · III",
113
- "row:bodies": "Mundo · Luna · Estrella",
114
- "row:snapshotRecall": "Recuperar instantánea",
115
- "row:snapshotStore": "Guardar instantánea",
116
- "row:jamming": "Jamming",
117
- "row:playback": "Reproducción",
118
- "row:cosmicLfo": "Cosmic LFO",
119
- "row:sensors": "Sensores",
120
- "row:midiLearn": "MIDI learn",
121
- "row:performanceMode": "Modo rendimiento",
122
- })
123
-
124
- const NAMES: Readonly<Record<string, Names>> = Object.freeze({ es: ES })
125
-
126
- /** An action's name in a language (`es`, `es-AR`), falling back to the catalogue's English. */
127
- export function shortcutLabel(action: Pick<ShortcutAction, 'id' | 'label'>, locale?: string): string {
128
- return NAMES[(locale ?? '').slice(0, 2).toLowerCase()]?.[action.id] ?? action.label
129
- }
130
-
131
- /** A section's name in a language, falling back to the English. */
132
- export function shortcutSectionLabel(section: { id: string; label: string }, locale?: string): string {
133
- return NAMES[(locale ?? '').slice(0, 2).toLowerCase()]?.[`section:${section.id}`] ?? section.label
134
- }
135
-
136
- /** Any other named thing of the shortcuts (a command group `group:<id>`, a sheet row `row:<id>`). */
137
- export function shortcutName(key: string, english: string, locale?: string): string {
138
- return NAMES[(locale ?? '').slice(0, 2).toLowerCase()]?.[key] ?? english
139
- }
@@ -1,132 +0,0 @@
1
- // The Orbiters catalogues: every key Orbiters answers, with the keys it has by default.
2
- //
3
- // `voice` acts on the focused deck(s), so in a shared session it only reaches a deck this person
4
- // holds; `client` is about this machine and no deck (MIDI learn, performance mode, the Studio).
5
-
6
- import type { ShortcutAction } from './match.js'
7
-
8
- export type OrbitersShortcutScope = 'voice' | 'client'
9
- export type OrbitersShortcut = ShortcutAction<OrbitersShortcutScope>
10
-
11
- export const ORBITERS_PLAY_SECTION = 'orbiters-play'
12
- export const ORBITERS_STUDIO_SECTION = 'orbiters-studio'
13
-
14
- interface ActionOptions {
15
- scope?: OrbitersShortcutScope
16
- group?: string
17
- command?: boolean
18
- }
19
-
20
- const play = (id: string, label: string, defaultKeys: readonly string[], { scope = 'voice', group, command }: ActionOptions = {}): OrbitersShortcut =>
21
- Object.freeze({
22
- id: `orbiters.play.${id}`, section: ORBITERS_PLAY_SECTION, label, defaultKeys: Object.freeze([...defaultKeys]), scope,
23
- ...(group ? { group } : {}), ...(command === false ? { command } : {}),
24
- })
25
-
26
- const studio = (id: string, label: string, defaultKeys: readonly string[]): OrbitersShortcut =>
27
- Object.freeze({ id: `orbiters.studio.${id}`, section: ORBITERS_STUDIO_SECTION, label, defaultKeys: Object.freeze([...defaultKeys]), scope: 'client', group: 'studio' })
28
-
29
- /** How a command menu groups the actions, in its order. */
30
- export const ORBITERS_COMMAND_GROUPS = Object.freeze([
31
- { id: 'transport', label: 'Transport' },
32
- { id: 'view', label: 'View' },
33
- { id: 'snapshots', label: 'Snapshots' },
34
- { id: 'panels', label: 'Panels' },
35
- { id: 'orbiter', label: 'Orbiter' },
36
- { id: 'studio', label: 'Studio' },
37
- ] as const)
38
-
39
- /** The snapshot slots, by the number each shows: the number row, 1 to 9, then 0. */
40
- const SLOT_NUMBERS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
41
-
42
- /** Undo and redo, the same keys wherever an edit can be taken back. */
43
- const UNDO_KEYS = ['Mod+KeyZ']
44
- const REDO_KEYS = ['Mod+Shift+KeyZ', 'Ctrl+Shift?+KeyY']
45
-
46
- /**
47
- * Playing an Orbiter. Shift is free on most keys (`Shift?`): it is held to select several decks,
48
- * and a key pressed while selecting means what it means alone.
49
- */
50
- export const ORBITERS_PLAY_SHORTCUTS: readonly OrbitersShortcut[] = Object.freeze([
51
- // Space takes Ctrl, Option and ⌘ as it comes: only Shift changes what it does.
52
- play('transport.toggle', 'Play or pause', ['Alt?+Ctrl?+Meta?+Space'], { group: 'transport' }),
53
- play('transport.restart', 'Play from the beginning', ['Shift+Alt?+Ctrl?+Meta?+Space'], { group: 'transport' }),
54
- play('rec.hold', 'Record while held', ['KeyR'], { group: 'transport', command: false }),
55
- play('rec.toggle', 'Rec on or off', ['Shift+KeyR'], { group: 'transport' }),
56
- play('history.undo', 'Undo', UNDO_KEYS, { group: 'transport' }),
57
- play('history.redo', 'Redo', REDO_KEYS, { group: 'transport' }),
58
- play('dimension.1', 'First dimension', ['Shift?+Digit1'], { group: 'view' }),
59
- play('dimension.2', 'Second dimension', ['Shift?+Digit2'], { group: 'view' }),
60
- play('dimension.3', 'Third dimension', ['Shift?+Digit3'], { group: 'view' }),
61
- play('body.world', 'World', ['Shift?+KeyQ'], { group: 'view' }),
62
- play('body.moon', 'Moon', ['Shift?+KeyW'], { group: 'view' }),
63
- play('body.star', 'Star', ['Shift?+KeyE'], { group: 'view' }),
64
- ...SLOT_NUMBERS.map((n) => play(`snapshot.recall.${n}`, `Recall snapshot ${n}`, [`Alt+Digit${n}`], { group: 'snapshots' })),
65
- ...SLOT_NUMBERS.map((n) => play(`snapshot.store.${n}`, `Store snapshot ${n}`, [`Alt+Shift+Digit${n}`], { group: 'snapshots' })),
66
- // The panels, from the bottom of the rail up.
67
- play('panel.jamming', 'Jamming', ['KeyA'], { group: 'panels' }),
68
- play('panel.playback', 'Playback', ['KeyS'], { group: 'panels' }),
69
- play('panel.cosmicLfo', 'Cosmic LFO', ['KeyD'], { group: 'panels' }),
70
- play('panel.sensors', 'Sensors', ['KeyF'], { group: 'panels' }),
71
- // The More menu, and this machine's switches.
72
- play('midiLearn', 'MIDI learn', ['Shift?+KeyM'], { scope: 'client', group: 'orbiter' }),
73
- play('performanceMode', 'Performance mode', ['Shift?+KeyP'], { scope: 'client', group: 'orbiter' }),
74
- play('screenCapture', 'Screen capture', [], { scope: 'client', group: 'orbiter' }),
75
- play('share', 'Share', [], { scope: 'client', group: 'orbiter' }),
76
- play('fullscreen', 'Fullscreen', [], { scope: 'client', group: 'orbiter' }),
77
- // `?` on a US keyboard: the key's place, whatever this keyboard prints there.
78
- play('help', 'Keyboard shortcuts', ['Shift?+Slash'], { scope: 'client', group: 'orbiter', command: false }),
79
- play('commands', 'Commands', ['Mod+KeyK'], { scope: 'client', group: 'orbiter', command: false }),
80
- ])
81
-
82
- /**
83
- * The keys sheet (`?`): what to look up at a glance, in three columns. A row may stand for several
84
- * actions whose keys differ only in the last key ("Dimension I · II · III — 1 2 3"); the keys
85
- * shown are always this person's (`mergedKeyParts`).
86
- */
87
- export const ORBITERS_PLAY_SHEET = Object.freeze([
88
- { id: 'transport', label: 'Transport', rows: [
89
- { id: 'playPause', label: 'Play / pause', actions: ['transport.toggle'] },
90
- { id: 'playFromStart', label: 'Play from start', actions: ['transport.restart'] },
91
- { id: 'recHeld', label: 'REC while held', actions: ['rec.hold'] },
92
- { id: 'recLatch', label: 'REC latch', actions: ['rec.toggle'] },
93
- { id: 'undo', label: 'Undo', actions: ['history.undo'] },
94
- { id: 'redo', label: 'Redo', actions: ['history.redo'] },
95
- ] },
96
- { id: 'view', label: 'View', rows: [
97
- { id: 'dimensions', label: 'Dimension I · II · III', actions: ['dimension.1', 'dimension.2', 'dimension.3'] },
98
- { id: 'bodies', label: 'World · Moon · Star', actions: ['body.world', 'body.moon', 'body.star'] },
99
- { id: 'snapshotRecall', label: 'Snapshot recall', actions: SLOT_NUMBERS.map((n) => `snapshot.recall.${n}`) },
100
- { id: 'snapshotStore', label: 'Snapshot store', actions: SLOT_NUMBERS.map((n) => `snapshot.store.${n}`) },
101
- ] },
102
- { id: 'panels', label: 'Panels', rows: [
103
- { id: 'jamming', label: 'Jamming', actions: ['panel.jamming'] },
104
- { id: 'playback', label: 'Playback', actions: ['panel.playback'] },
105
- { id: 'cosmicLfo', label: 'Cosmic LFO', actions: ['panel.cosmicLfo'] },
106
- { id: 'sensors', label: 'Sensors', actions: ['panel.sensors'] },
107
- { id: 'midiLearn', label: 'MIDI learn', actions: ['midiLearn'] },
108
- { id: 'performanceMode', label: 'Performance mode', actions: ['performanceMode'] },
109
- ] },
110
- ].map((column) => Object.freeze({
111
- ...column,
112
- rows: Object.freeze(column.rows.map((row) => Object.freeze({ ...row, actions: Object.freeze(row.actions.map((a) => `orbiters.play.${a}`)) }))),
113
- })))
114
-
115
- /**
116
- * The Studio. It is open over play, so the play keys above still work there; where both name a key,
117
- * the Studio's action is the one.
118
- */
119
- export const ORBITERS_STUDIO_SHORTCUTS: readonly OrbitersShortcut[] = Object.freeze([
120
- studio('undo', 'Undo', UNDO_KEYS),
121
- studio('redo', 'Redo', REDO_KEYS),
122
- studio('map', 'Map', ['Shift+KeyM']),
123
- ])
124
-
125
- /**
126
- * The letters the on-screen keyboard plays while it is open (`event.key`, lower-case): the piano row,
127
- * then Z and X for the octave and C and V for velocity. While it is up they are its notes, and the
128
- * shortcuts on them wait (`claimKeys`); a settings page says so beside those keys.
129
- */
130
- export const ORBITERS_KEYBOARD_SURFACE_KEYS: readonly string[] = Object.freeze([
131
- 'a', 'w', 's', 'e', 'd', 'f', 't', 'g', 'y', 'h', 'u', 'j', 'k', 'o', 'l', 'p', ';', "'", 'z', 'x', 'c', 'v',
132
- ])
@@ -1,110 +0,0 @@
1
- // A person's shortcut overrides, read the same way by every app: from their account when signed in
2
- // (someone signed out has the default keys), and again whenever the page comes back to the front (a key changed
3
- // on the settings page applies on return, without a reload).
4
- //
5
- // Framework-free: the app hands in how it reads the account (its own API client) and gets the
6
- // overrides and each catalogue's resolved keys back, kept until the overrides change.
7
-
8
- import { resolveBindings, type ShortcutAction, type ShortcutOverrides } from './match.js'
9
-
10
- /** The overrides in a stored value, or `null` when it is not a list of `{ id, keys }`. */
11
- export function parseOverrides(value: unknown): ShortcutOverrides | null {
12
- if (!Array.isArray(value)) return null
13
- return value.filter(
14
- (entry): entry is { id: string; keys: string[] } =>
15
- !!entry && typeof entry.id === 'string' && Array.isArray(entry.keys) && entry.keys.every((k: unknown) => typeof k === 'string'),
16
- )
17
- }
18
-
19
- /**
20
- * How the app reads the signed-in person's settings: `null` when no one is signed in (the
21
- * default keys); `undefined` when the read failed (the overrides in force are kept: a blip must not
22
- * swap someone's keys for the defaults).
23
- */
24
- export type ReadAccountSettings = (options: { fresh: boolean }) => Promise<{ shortcuts?: unknown } | null | undefined>
25
-
26
- export interface ShortcutOverridesSource {
27
- /** The overrides in force. */
28
- get(): ShortcutOverrides
29
- /** A catalogue's actions with their keys now (kept until the overrides change). */
30
- bindings(catalogue: readonly ShortcutAction[]): Map<string, readonly string[]>
31
- /** Called whenever the overrides change; returns the unsubscribe. */
32
- subscribe(listener: () => void): () => void
33
- /** Read again (`fresh`: past the app's own cached read). The newest read wins, whatever order they end in. */
34
- refresh(options?: { fresh?: boolean }): Promise<void>
35
- /** Read now, and again whenever the page comes back to the front; once per source. */
36
- bind(): void
37
- /** For tests: set the overrides directly. */
38
- set(overrides: ShortcutOverrides): void
39
- }
40
-
41
- /** Coming back to the front twice in a row (visible, then focus) asks the account once. */
42
- const REFRESH_GAP_MS = 2000
43
-
44
- export function createShortcutOverridesSource(readAccount: ReadAccountSettings): ShortcutOverridesSource {
45
- let overrides: ShortcutOverrides = []
46
- let resolved = new WeakMap<readonly ShortcutAction[], Map<string, readonly string[]>>()
47
- let latest = 0
48
- let applied = 0
49
- let lastRefreshAt = -Infinity
50
- let bound = false
51
- const listeners = new Set<() => void>()
52
-
53
- const apply = (next: ShortcutOverrides) => {
54
- if (JSON.stringify(next) === JSON.stringify(overrides)) return
55
- overrides = next
56
- resolved = new WeakMap()
57
- listeners.forEach((listener) => listener())
58
- }
59
-
60
- const refresh = async ({ fresh = false }: { fresh?: boolean } = {}) => {
61
- const ticket = ++latest
62
- lastRefreshAt = Date.now()
63
- let settings: { shortcuts?: unknown } | null | undefined
64
- try {
65
- settings = await readAccount({ fresh })
66
- } catch {
67
- settings = undefined
68
- }
69
- // The newest read that answered wins: an older one ending after it says nothing new, and a newer
70
- // one that failed does not throw away an older one's answer.
71
- if (settings === undefined || ticket < applied) return
72
- applied = ticket
73
- apply(settings === null ? [] : (parseOverrides(settings.shortcuts) ?? []))
74
- }
75
-
76
- return {
77
- get: () => overrides,
78
- bindings(catalogue) {
79
- let map = resolved.get(catalogue)
80
- if (!map) {
81
- map = resolveBindings(catalogue, overrides)
82
- resolved.set(catalogue, map)
83
- }
84
- return map
85
- },
86
- subscribe(listener) {
87
- listeners.add(listener)
88
- return () => {
89
- listeners.delete(listener)
90
- }
91
- },
92
- refresh,
93
- bind() {
94
- const win = globalThis as unknown as {
95
- addEventListener?: (type: string, fn: () => void) => void
96
- document?: { visibilityState?: string; addEventListener?: (type: string, fn: () => void) => void }
97
- }
98
- if (bound || typeof win.addEventListener !== 'function' || !win.document?.addEventListener) return
99
- bound = true
100
- void refresh()
101
- const onFront = () => {
102
- if (win.document?.visibilityState !== 'visible' || Date.now() - lastRefreshAt < REFRESH_GAP_MS) return
103
- void refresh({ fresh: true })
104
- }
105
- win.document.addEventListener('visibilitychange', onFront)
106
- win.addEventListener('focus', onFront)
107
- },
108
- set: apply,
109
- }
110
- }
@@ -1,36 +0,0 @@
1
- // Every app's catalogue in one place, in the order a settings page shows them. A section is one
2
- // surface's keys; two actions may share a key only across sections (the Orbiter Studio lies over
3
- // play, and takes ⌘Z from it while it is open).
4
-
5
- import type { ShortcutAction } from './match.js'
6
- import { EW_PLAYER_SECTION, EW_PLAYER_SHORTCUTS, EW_STUDIO_SECTION, EW_STUDIO_SHORTCUTS } from './entangledWorlds.js'
7
- import { ORBITERS_PLAY_SECTION, ORBITERS_PLAY_SHORTCUTS, ORBITERS_STUDIO_SECTION, ORBITERS_STUDIO_SHORTCUTS } from './orbiters.js'
8
- import { SITE_SECTION, SITE_SHORTCUTS } from './site.js'
9
-
10
- /** The apps a person tells apart, in the order a settings page shows them. */
11
- export const SHORTCUT_APPS = Object.freeze([
12
- Object.freeze({ id: 'plantasia-space', label: 'Plantasia Space' }),
13
- Object.freeze({ id: 'orbiters', label: 'Orbiters' }),
14
- Object.freeze({ id: 'entangled-worlds', label: 'Entangled Worlds' }),
15
- ] as const)
16
-
17
- export type ShortcutAppId = (typeof SHORTCUT_APPS)[number]['id']
18
-
19
- export interface ShortcutSection {
20
- readonly id: string
21
- readonly label: string
22
- /** The app it belongs to. */
23
- readonly app: ShortcutAppId
24
- /** Its name inside that app (`Play`, `Studio`); none when the app has only this section. */
25
- readonly part?: string
26
- readonly actions: readonly ShortcutAction[]
27
- }
28
-
29
- export const SHORTCUT_SECTIONS: readonly ShortcutSection[] = Object.freeze<ShortcutSection[]>([
30
- Object.freeze({ id: SITE_SECTION, label: 'Plantasia Space', app: 'plantasia-space', actions: SITE_SHORTCUTS }),
31
- Object.freeze({ id: ORBITERS_PLAY_SECTION, label: 'Orbiters play', app: 'orbiters', part: 'Play', actions: ORBITERS_PLAY_SHORTCUTS }),
32
- Object.freeze({ id: ORBITERS_STUDIO_SECTION, label: 'Orbiter Studio', app: 'orbiters', part: 'Studio', actions: ORBITERS_STUDIO_SHORTCUTS }),
33
- Object.freeze({ id: EW_STUDIO_SECTION, label: 'Entangled Worlds studio', app: 'entangled-worlds', part: 'Studio', actions: EW_STUDIO_SHORTCUTS }),
34
- Object.freeze({ id: EW_PLAYER_SECTION, label: 'Entangled Worlds player', app: 'entangled-worlds', part: 'Player', actions: EW_PLAYER_SHORTCUTS }),
35
- ])
36
-
@@ -1,33 +0,0 @@
1
- // The Plantasia Space site's own keys: the pages around the apps (home, explore, settings). They
2
- // work on any page of the site, outside an Orbiter or a world.
3
-
4
- import type { ShortcutAction } from './match.js'
5
-
6
- export type SiteShortcut = ShortcutAction<'client'>
7
-
8
- export const SITE_SECTION = 'site'
9
-
10
- const site = (id: string, label: string, keys: string): SiteShortcut =>
11
- Object.freeze({ id: `site.${id}`, section: SITE_SECTION, label, defaultKeys: Object.freeze([keys]), scope: 'client' as const })
12
-
13
- /**
14
- * Two-key sequences move around the site (G, then H for Home; N, then O for a new Orbiter), as on
15
- * many sites: a single letter is an Orbiter's on a page that plays one, and the browser holds most
16
- * ⌘ and ⇧⌘ letters.
17
- */
18
- export const SITE_SHORTCUTS: readonly SiteShortcut[] = Object.freeze([
19
- site('settings', 'Open settings', 'Mod+Shift+Comma'),
20
- site('commands', 'Commands', 'Mod+KeyK'),
21
- site('search', 'Search', 'Slash'),
22
- site('go.home', 'Home', 'KeyG KeyH'),
23
- site('go.playground', 'Playground', 'KeyG KeyP'),
24
- site('go.notifications', 'Notifications', 'KeyG KeyN'),
25
- site('go.audios', 'My audios', 'KeyG KeyA'),
26
- site('go.orbiters', 'My Orbiters', 'KeyG KeyO'),
27
- site('go.worlds', 'My Entangled Worlds', 'KeyG KeyW'),
28
- site('go.collections', 'My collections', 'KeyG KeyC'),
29
- site('go.stars', 'My stars', 'KeyG KeyS'),
30
- site('new.orbiter', 'New Orbiter', 'KeyN KeyO'),
31
- site('new.audio', 'New audio', 'KeyN KeyA'),
32
- site('new.world', 'New Entangled World', 'KeyN KeyW'),
33
- ])