Unified · Split
| @@ -74,6 +74,7 @@ import { detectComposerTrigger, replaceTextRange } from "@t3tools/shared/compose · 73 unmodified lines | |||
| 74 | import { ComposerCommandPopover, type ComposerCommandItem } from "./ComposerCommandPopover"; | 74 | import { ComposerCommandPopover, type ComposerCommandItem } from "./ComposerCommandPopover"; |
| 75 | import { | 75 | import { |
| 76 | buildMobilePluginCommandItems, | 76 | buildMobilePluginCommandItems, |
| 77 | isCollapsedComposerSelection, | ||
| 77 | reconcileComposerSelectionForTextChange, | 78 | reconcileComposerSelectionForTextChange, |
| 78 | } from "./plugin-command-menu"; | 79 | } from "./plugin-command-menu"; |
| 79 | import { useMobilePluginCommands } from "./use-mobile-plugin-commands"; | 80 | import { useMobilePluginCommands } from "./use-mobile-plugin-commands"; |
| @@ -146,9 +147,10 @@ export function NewTaskDraftScreen(props: { · 66 unmodified lines | |||
| 146 | selectedProject?.environmentId ?? null, | 147 | selectedProject?.environmentId ?? null, |
| 147 | ); | 148 | ); |
| 148 | const pluginCommandTrigger = useMemo(() => { | 149 | const pluginCommandTrigger = useMemo(() => { |
| 150 | if (!isCollapsedComposerSelection(composerSelection)) return null; | ||
| 149 | const trigger = detectComposerTrigger(flow.prompt, composerSelection.end); | 151 | const trigger = detectComposerTrigger(flow.prompt, composerSelection.end); |
| 150 | return trigger?.kind === "slash-command" ? trigger : null; | 152 | return trigger?.kind === "slash-command" ? trigger : null; |
| 151 | }, [composerSelection.end, flow.prompt]); | 153 | }, [composerSelection, flow.prompt]); |
| 152 | const pluginCommandItems = useMemo<ComposerCommandItem[]>(() => { | 154 | const pluginCommandItems = useMemo<ComposerCommandItem[]>(() => { |
| 153 | if (pluginCommandTrigger === null) return []; | 155 | if (pluginCommandTrigger === null) return []; |
| 154 | return buildMobilePluginCommandItems(pluginCommands, pluginCommandTrigger.query); | 156 | return buildMobilePluginCommandItems(pluginCommands, pluginCommandTrigger.query); |
| @@ -2,6 +2,7 @@ import { describe, expect, it } from "vite-plus/test"; · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | import { | 3 | import { |
| 4 | buildMobilePluginCommandItems, | 4 | buildMobilePluginCommandItems, |
| 5 | isCollapsedComposerSelection, | ||
| 5 | reconcileComposerSelectionForTextChange, | 6 | reconcileComposerSelectionForTextChange, |
| 6 | } from "./plugin-command-menu"; | 7 | } from "./plugin-command-menu"; |
| 7 | 8 | ||
| @@ -37,3 +38,9 @@ describe("reconcileComposerSelectionForTextChange", () => { · 29 unmodified lines | |||
| 37 | }); | 38 | }); |
| 38 | }); | 39 | }); |
| 39 | }); | 40 | }); |
| 41 | |||
| 42 | describe("isCollapsedComposerSelection", () => { | ||
| 43 | it("rejects a highlighted range", () => { | ||
| 44 | expect(isCollapsedComposerSelection({ start: 0, end: 7 })).toBe(false); | ||
| 45 | }); | ||
| 46 | }); | ||
| @@ -2,6 +2,11 @@ import type { PluginCommand } from "@t3tools/contracts"; · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | import type { ComposerCommandItem } from "./ComposerCommandPopover"; | 3 | import type { ComposerCommandItem } from "./ComposerCommandPopover"; |
| 4 | 4 | ||
| 5 | export const isCollapsedComposerSelection = (selection: { | ||
| 6 | readonly start: number; | ||
| 7 | readonly end: number; | ||
| 8 | }): boolean => selection.start === selection.end; | ||
| 9 | |||
| 5 | export function reconcileComposerSelectionForTextChange( | 10 | export function reconcileComposerSelectionForTextChange( |
| 6 | selection: { readonly start: number; readonly end: number }, | 11 | selection: { readonly start: number; readonly end: number }, |
| 7 | previousLength: number, | 12 | previousLength: number, |
| @@ -55,8 +55,12 @@ type PluginCommandHandler = Effect.Effect< · 54 unmodified lines | |||
| 55 | 55 | ||
| 56 | export class PluginCommandDefinitionError extends Schema.TaggedErrorClass<PluginCommandDefinitionError>()( | 56 | export class PluginCommandDefinitionError extends Schema.TaggedErrorClass<PluginCommandDefinitionError>()( |
| 57 | "PluginCommandDefinitionError", | 57 | "PluginCommandDefinitionError", |
| 58 | { message: Schema.String }, | 58 | { cause: Schema.Defect(), id: Schema.String }, |
| 59 | ) {} | 59 | ) { |
| 60 | override get message(): string { | ||
| 61 | return `Plugin command ${this.id} has invalid declarative metadata.`; | ||
| 62 | } | ||
| 63 | } | ||
| 60 | 64 | ||
| 61 | export interface PluginCommandRegistration { | 65 | export interface PluginCommandRegistration { |
| 62 | readonly command: PluginCommand; | 66 | readonly command: PluginCommand; |
| @@ -108,12 +112,7 @@ const catalogFromRuntime = Effect.fn("PluginCommandCatalog.catalogFromRuntime")( · 45 unmodified lines | |||
| 108 | const snapshot = yield* runtime.contributions(COMMAND_SLOT); | 112 | const snapshot = yield* runtime.contributions(COMMAND_SLOT); |
| 109 | const commands = yield* Effect.forEach(snapshot.entries, (entry) => | 113 | const commands = yield* Effect.forEach(snapshot.entries, (entry) => |
| 110 | decodePluginCommandEffect(commandInputFromContribution(entry)).pipe( | 114 | decodePluginCommandEffect(commandInputFromContribution(entry)).pipe( |
| 111 | Effect.mapError( | 115 | Effect.mapError((cause) => new PluginCommandDefinitionError({ cause, id: entry.id })), |
| 112 | () => | ||
| 113 | new PluginCommandDefinitionError({ | ||
| 114 | message: `Plugin command ${entry.id} has invalid declarative metadata.`, | ||
| 115 | }), | ||
| 116 | ), | ||
| 117 | ), | 116 | ), |
| 118 | ); | 117 | ); |
| 119 | const frozenCommands = commands.map((command) => | 118 | const frozenCommands = commands.map((command) => |
| @@ -197,6 +196,7 @@ export const make = Effect.gen(function* () { · 77 unmodified lines | |||
| 197 | return new PluginCommandNotFoundError({ id: input.id }); | 196 | return new PluginCommandNotFoundError({ id: input.id }); |
| 198 | } | 197 | } |
| 199 | return new PluginCommandInvocationError({ | 198 | return new PluginCommandInvocationError({ |
| 199 | cause: error, | ||
| 200 | id: input.id, | 200 | id: input.id, |
| 201 | message: "Plugin command failed.", | 201 | message: "Plugin command failed.", |
| 202 | }); | 202 | }); |
| @@ -58,6 +58,7 @@ export class PluginCommandCatalogChangedError extends Schema.TaggedErrorClass<Pl · 57 unmodified lines | |||
| 58 | export class PluginCommandInvocationError extends Schema.TaggedErrorClass<PluginCommandInvocationError>()( | 58 | export class PluginCommandInvocationError extends Schema.TaggedErrorClass<PluginCommandInvocationError>()( |
| 59 | "PluginCommandInvocationError", | 59 | "PluginCommandInvocationError", |
| 60 | { | 60 | { |
| 61 | cause: Schema.Defect(), | ||
| 61 | id: PluginCommandId, | 62 | id: PluginCommandId, |
| 62 | message: TrimmedNonEmptyString.check(Schema.isMaxLength(500)), | 63 | message: TrimmedNonEmptyString.check(Schema.isMaxLength(500)), |
| 63 | }, | 64 | }, |