188 lines · 5.5 KB · TypeScript
RawDownload
import * as Schema from "effect/Schema";import { ForwardCompatibleArray, TrimmedString } from "./baseSchemas.ts"; export const MAX_KEYBINDING_VALUE_LENGTH = 64;const MAX_KEYBINDING_WHEN_LENGTH = 256;export const MAX_WHEN_EXPRESSION_DEPTH = 64;export const MAX_SCRIPT_ID_LENGTH = 24;export const MAX_KEYBINDINGS_COUNT = 256; export const THREAD_JUMP_KEYBINDING_COMMANDS = [  "thread.jump.1",  "thread.jump.2",  "thread.jump.3",  "thread.jump.4",  "thread.jump.5",  "thread.jump.6",  "thread.jump.7",  "thread.jump.8",  "thread.jump.9",] as const;export type ThreadJumpKeybindingCommand = (typeof THREAD_JUMP_KEYBINDING_COMMANDS)[number]; export const MODEL_PICKER_JUMP_KEYBINDING_COMMANDS = [  "modelPicker.jump.1",  "modelPicker.jump.2",  "modelPicker.jump.3",  "modelPicker.jump.4",  "modelPicker.jump.5",  "modelPicker.jump.6",  "modelPicker.jump.7",  "modelPicker.jump.8",  "modelPicker.jump.9",] as const;export type ModelPickerJumpKeybindingCommand =  (typeof MODEL_PICKER_JUMP_KEYBINDING_COMMANDS)[number]; const THREAD_KEYBINDING_COMMANDS = [  "thread.stop",  "thread.previous",  "thread.next",  "thread.copyReference",  "thread.settle",  "thread.pin",  ...THREAD_JUMP_KEYBINDING_COMMANDS,] as const;export type ThreadKeybindingCommand = (typeof THREAD_KEYBINDING_COMMANDS)[number]; const MODEL_PICKER_KEYBINDING_COMMANDS = [  "modelPicker.toggle",  ...MODEL_PICKER_JUMP_KEYBINDING_COMMANDS,] as const;export type ModelPickerKeybindingCommand = (typeof MODEL_PICKER_KEYBINDING_COMMANDS)[number]; export const STATIC_KEYBINDING_COMMANDS = [  "sidebar.toggle",  "terminal.toggle",  "terminal.split",  "terminal.splitVertical",  "terminal.new",  "terminal.close",  "rightPanel.toggle",  "rightPanel.toggleMaximized",  "rightPanel.close",  "diff.toggle",  "preview.toggle",  "preview.refresh",  "preview.focusUrl",  "preview.zoomIn",  "preview.zoomOut",  "preview.resetZoom",  "commandPalette.toggle",  "filePicker.toggle",  "projectSearch.toggle",  "themeEditor.toggle",  "composer.stash",  "chat.new",  "chat.newLocal",  "editor.openFavorite",  ...MODEL_PICKER_KEYBINDING_COMMANDS,  ...THREAD_KEYBINDING_COMMANDS,] as const; export const SCRIPT_RUN_COMMAND_PATTERN = Schema.TemplateLiteral([  Schema.Literal("script."),  Schema.NonEmptyString.check(    Schema.isMaxLength(MAX_SCRIPT_ID_LENGTH),    Schema.isPattern(/^[a-z0-9][a-z0-9-]*$/),  ),  Schema.Literal(".run"),]); export const KeybindingCommand = Schema.Union([  Schema.Literals(STATIC_KEYBINDING_COMMANDS),  SCRIPT_RUN_COMMAND_PATTERN,]);export type KeybindingCommand = typeof KeybindingCommand.Type; export const KeybindingValue = TrimmedString.check(  Schema.isMinLength(1),  Schema.isMaxLength(MAX_KEYBINDING_VALUE_LENGTH),); export const KeybindingWhen = TrimmedString.check(  Schema.isMinLength(1),  Schema.isMaxLength(MAX_KEYBINDING_WHEN_LENGTH),);export const KeybindingRule = Schema.Struct({  key: KeybindingValue,  command: KeybindingCommand,  when: Schema.optional(KeybindingWhen),});export type KeybindingRule = typeof KeybindingRule.Type; export const KeybindingsConfig = Schema.Array(KeybindingRule).check(  Schema.isMaxLength(MAX_KEYBINDINGS_COUNT),);export type KeybindingsConfig = typeof KeybindingsConfig.Type; export const KeybindingShortcut = Schema.Struct({  key: KeybindingValue,  metaKey: Schema.Boolean,  ctrlKey: Schema.Boolean,  shiftKey: Schema.Boolean,  altKey: Schema.Boolean,  modKey: Schema.Boolean,});export type KeybindingShortcut = typeof KeybindingShortcut.Type; const KeybindingWhenNodeRef = Schema.suspend(  (): Schema.Codec<KeybindingWhenNode> => KeybindingWhenNode,);export const KeybindingWhenNode = Schema.Union([  Schema.Struct({    type: Schema.Literal("identifier"),    name: Schema.NonEmptyString,  }),  Schema.Struct({    type: Schema.Literal("not"),    node: KeybindingWhenNodeRef,  }),  Schema.Struct({    type: Schema.Literal("and"),    left: KeybindingWhenNodeRef,    right: KeybindingWhenNodeRef,  }),  Schema.Struct({    type: Schema.Literal("or"),    left: KeybindingWhenNodeRef,    right: KeybindingWhenNodeRef,  }),]);export type KeybindingWhenNode =  | { type: "identifier"; name: string }  | { type: "not"; node: KeybindingWhenNode }  | { type: "and"; left: KeybindingWhenNode; right: KeybindingWhenNode }  | { type: "or"; left: KeybindingWhenNode; right: KeybindingWhenNode }; export const ResolvedKeybindingRule = Schema.Struct({  command: KeybindingCommand,  shortcut: KeybindingShortcut,  whenAst: Schema.optional(KeybindingWhenNode),}).annotate({ parseOptions: { onExcessProperty: "ignore" } });export type ResolvedKeybindingRule = typeof ResolvedKeybindingRule.Type; /** * The command set grows over time, so a client may receive rules it cannot * represent (a command or `when` node added after that client shipped). * Decoding drops those rules instead of failing the whole payload — * rejecting the config would take down the connection over a shortcut the * client couldn't dispatch anyway. */export const ResolvedKeybindingsConfig = ForwardCompatibleArray(ResolvedKeybindingRule).check(  Schema.isMaxLength(MAX_KEYBINDINGS_COUNT),);export type ResolvedKeybindingsConfig = typeof ResolvedKeybindingsConfig.Type; export class KeybindingsConfigError extends Schema.TaggedError<KeybindingsConfigError>()(  "KeybindingsConfigParseError",  {    configPath: Schema.String,    detail: Schema.String,    cause: Schema.optional(Schema.Defect()),  },) {  override get message(): string {    return `Unable to parse keybindings config at ${this.configPath}: ${this.detail}`;  }}