lumiverse-spindle-types 0.3.8 → 0.3.9

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lumiverse-spindle-types",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "types": "./src/index.ts",
5
5
  "keywords": [
6
6
  "lumiverse",
package/src/api.ts CHANGED
@@ -640,6 +640,53 @@ export type SpindleModalItemDTO =
640
640
  /** A themed card/container that groups child items. */
641
641
  | { type: "card"; items: SpindleModalItemDTO[] };
642
642
 
643
+ // ─── Command Palette DTOs ──────────────────────────────────────────────
644
+
645
+ /**
646
+ * Command registration payload sent by extensions to add entries
647
+ * to the Lumiverse command palette (Cmd/Ctrl+K).
648
+ *
649
+ * Commands are contextual — extensions can register different sets
650
+ * based on the current chat, page, or app state by calling
651
+ * `spindle.commands.register()` with an updated list at any time.
652
+ * Each call replaces all previously registered commands from that extension.
653
+ */
654
+ export interface SpindleCommandDTO {
655
+ /** Unique identifier for this command within the extension (e.g. `"summarize-chat"`). */
656
+ id: string;
657
+ /** Display label shown in the command palette. Max 80 characters. */
658
+ label: string;
659
+ /** Description shown below the label. Max 200 characters. */
660
+ description: string;
661
+ /** Optional search keywords for fuzzy matching. Max 10 keywords, 30 chars each. */
662
+ keywords?: string[];
663
+ /**
664
+ * Scope restriction controlling when the command appears.
665
+ * - `'global'` — always visible (default)
666
+ * - `'chat'` — only when viewing a chat
667
+ * - `'chat-idle'` — only when in a chat and not streaming
668
+ * - `'landing'` — only on the home page
669
+ * - `'character'` — only on character pages
670
+ */
671
+ scope?: "global" | "chat" | "chat-idle" | "landing" | "character";
672
+ }
673
+
674
+ /**
675
+ * Context snapshot sent to the extension when a command is invoked
676
+ * from the command palette. Contains the frontend's current UI state
677
+ * so the extension can act on the right chat/character/page.
678
+ */
679
+ export interface SpindleCommandContextDTO {
680
+ /** Current route path (e.g. `"/chat/abc-123"`, `"/"`, `"/characters/xyz"`). */
681
+ route: string;
682
+ /** Active chat ID, if the user is in a chat view. */
683
+ chatId?: string;
684
+ /** Active character ID, if available. */
685
+ characterId?: string;
686
+ /** Whether the active chat is a group chat. */
687
+ isGroupChat?: boolean;
688
+ }
689
+
643
690
  // ─── Worker → Host messages ──────────────────────────────────────────────
644
691
 
645
692
  export type WorkerToHost =
@@ -883,7 +930,10 @@ export type WorkerToHost =
883
930
  | { type: "theme_clear"; requestId: string; userId?: string }
884
931
  | { type: "theme_get_current"; requestId: string; userId?: string }
885
932
  // ─── Color Extraction (gated: "app_manipulation") ─────────────────────
886
- | { type: "color_extract"; requestId: string; imageId: string; userId?: string };
933
+ | { type: "color_extract"; requestId: string; imageId: string; userId?: string }
934
+ // ─── Commands (free tier) ──────────────────────────────────────────────
935
+ | { type: "commands_register"; commands: SpindleCommandDTO[] }
936
+ | { type: "commands_unregister"; commandIds: string[] };
887
937
 
888
938
  // ─── Host → Worker messages ──────────────────────────────────────────────
889
939
 
@@ -930,4 +980,10 @@ export type HostToWorker =
930
980
  type: "oauth_callback";
931
981
  requestId: string;
932
982
  params: Record<string, string>;
983
+ }
984
+ | {
985
+ type: "command_invoked";
986
+ commandId: string;
987
+ context: SpindleCommandContextDTO;
988
+ userId: string;
933
989
  };
package/src/index.ts CHANGED
@@ -57,6 +57,8 @@ export type {
57
57
  ColorRGB,
58
58
  ColorHSL,
59
59
  SpindleModalItemDTO,
60
+ SpindleCommandDTO,
61
+ SpindleCommandContextDTO,
60
62
  WorkerToHost,
61
63
  HostToWorker,
62
64
  } from "./api";
@@ -34,6 +34,8 @@ import type {
34
34
  ThemeInfoDTO,
35
35
  ColorExtractionResult,
36
36
  SpindleModalItemDTO,
37
+ SpindleCommandDTO,
38
+ SpindleCommandContextDTO,
37
39
  } from "./api";
38
40
 
39
41
  /** The global `spindle` object available in backend extension workers */
@@ -728,6 +730,71 @@ export interface SpindleAPI {
728
730
  }>;
729
731
  };
730
732
 
733
+ /**
734
+ * Command palette integration (free tier — no permission needed).
735
+ * Register commands that appear in the Lumiverse command palette
736
+ * (Cmd/Ctrl+K). Commands are contextual — call `register()` with
737
+ * an updated list whenever the available commands should change
738
+ * (e.g. based on active chat, character, or extension state).
739
+ *
740
+ * Each `register()` call **replaces** all previously registered
741
+ * commands from this extension. To add commands incrementally,
742
+ * maintain your own list and pass the full set each time.
743
+ *
744
+ * @example
745
+ * ```ts
746
+ * // Register commands
747
+ * spindle.commands.register([
748
+ * {
749
+ * id: 'summarize-chat',
750
+ * label: 'Summarize Chat',
751
+ * description: 'Generate a summary of the current conversation',
752
+ * keywords: ['summary', 'recap', 'tldr'],
753
+ * scope: 'chat',
754
+ * },
755
+ * ])
756
+ *
757
+ * // Handle invocations
758
+ * spindle.commands.onInvoked((commandId, context) => {
759
+ * if (commandId === 'summarize-chat') {
760
+ * // context.chatId, context.characterId, etc.
761
+ * }
762
+ * })
763
+ *
764
+ * // Update commands based on context
765
+ * spindle.on('CHAT_CHANGED', () => {
766
+ * spindle.commands.register(getCommandsForCurrentState())
767
+ * })
768
+ *
769
+ * // Remove all commands
770
+ * spindle.commands.unregister()
771
+ * ```
772
+ */
773
+ commands: {
774
+ /**
775
+ * Register (or replace) the extension's command palette entries.
776
+ * Each call replaces the full set — pass the complete list of
777
+ * commands you want visible. Max 20 commands per extension.
778
+ */
779
+ register(commands: SpindleCommandDTO[]): void;
780
+ /**
781
+ * Remove specific commands by ID, or all commands if no IDs given.
782
+ */
783
+ unregister(commandIds?: string[]): void;
784
+ /**
785
+ * Register a handler called when the user selects one of this
786
+ * extension's commands from the palette. The handler receives
787
+ * the command ID and a snapshot of the frontend's current state.
788
+ * Returns an unsubscribe function.
789
+ */
790
+ onInvoked(
791
+ handler: (
792
+ commandId: string,
793
+ context: SpindleCommandContextDTO,
794
+ ) => void | Promise<void>,
795
+ ): () => void;
796
+ };
797
+
731
798
  /** This extension's manifest */
732
799
  manifest: SpindleManifest;
733
800
  }