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