prompt_objects 0.6.0 → 0.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +126 -0
- data/README.md +93 -22
- data/exe/poop +5 -0
- data/exe/prompt_objects +1 -1108
- data/exe/prompt_objects_mcp +5 -3
- data/lib/prompt_objects/capability.rb +33 -0
- data/lib/prompt_objects/cli/diagnostics_command.rb +69 -0
- data/lib/prompt_objects/cli/env_command.rb +435 -0
- data/lib/prompt_objects/cli/events_command.rb +145 -0
- data/lib/prompt_objects/cli/explore_command.rb +155 -0
- data/lib/prompt_objects/cli/helpers.rb +91 -0
- data/lib/prompt_objects/cli/message_command.rb +216 -0
- data/lib/prompt_objects/cli/repl_command.rb +366 -0
- data/lib/prompt_objects/cli/serve_command.rb +162 -0
- data/lib/prompt_objects/cli.rb +64 -422
- data/lib/prompt_objects/connectors/base.rb +16 -29
- data/lib/prompt_objects/connectors/mcp.rb +82 -98
- data/lib/prompt_objects/domain/records.rb +45 -0
- data/lib/prompt_objects/environment/exporter.rb +5 -4
- data/lib/prompt_objects/environment/git.rb +21 -1
- data/lib/prompt_objects/environment/importer.rb +5 -4
- data/lib/prompt_objects/environment/manager.rb +44 -6
- data/lib/prompt_objects/environment/manifest.rb +26 -4
- data/lib/prompt_objects/environment.rb +1337 -20
- data/lib/prompt_objects/execution/blocking_executor.rb +88 -0
- data/lib/prompt_objects/execution/context.rb +137 -0
- data/lib/prompt_objects/execution/contracts.rb +59 -0
- data/lib/prompt_objects/execution/permit_pool.rb +49 -0
- data/lib/prompt_objects/execution/policy.rb +47 -0
- data/lib/prompt_objects/execution/resource_lock_manager.rb +63 -0
- data/lib/prompt_objects/execution/runner.rb +498 -0
- data/lib/prompt_objects/execution/scheduler.rb +325 -0
- data/lib/prompt_objects/llm/factory.rb +4 -4
- data/lib/prompt_objects/llm/gemini_adapter.rb +25 -8
- data/lib/prompt_objects/llm/openai_adapter.rb +9 -1
- data/lib/prompt_objects/llm/pricing.rb +9 -2
- data/lib/prompt_objects/llm/response.rb +9 -4
- data/lib/prompt_objects/loader.rb +28 -1
- data/lib/prompt_objects/mcp/server.rb +6 -3
- data/lib/prompt_objects/mcp/tools/get_conversation.rb +15 -11
- data/lib/prompt_objects/mcp/tools/get_pending_requests.rb +2 -2
- data/lib/prompt_objects/mcp/tools/respond_to_request.rb +3 -3
- data/lib/prompt_objects/mcp/tools/send_message.rb +42 -32
- data/lib/prompt_objects/operations/diagnostics.rb +115 -0
- data/lib/prompt_objects/operations/retention.rb +75 -0
- data/lib/prompt_objects/operations/run_trace_builder.rb +152 -0
- data/lib/prompt_objects/primitives/http_get.rb +2 -0
- data/lib/prompt_objects/primitives/list_files.rb +2 -0
- data/lib/prompt_objects/primitives/read_file.rb +2 -0
- data/lib/prompt_objects/primitives/think.rb +36 -0
- data/lib/prompt_objects/primitives/write_file.rb +9 -0
- data/lib/prompt_objects/prompt_object.rb +171 -519
- data/lib/prompt_objects/repositories/artifact_repository.rb +109 -0
- data/lib/prompt_objects/repositories/base_repository.rb +37 -0
- data/lib/prompt_objects/repositories/definition_repository.rb +36 -0
- data/lib/prompt_objects/repositories/env_data_repository.rb +103 -0
- data/lib/prompt_objects/repositories/event_repository.rb +105 -0
- data/lib/prompt_objects/repositories/message_repository.rb +73 -0
- data/lib/prompt_objects/repositories/run_repository.rb +254 -0
- data/lib/prompt_objects/repositories/thread_repository.rb +87 -0
- data/lib/prompt_objects/repositories/tool_execution_repository.rb +88 -0
- data/lib/prompt_objects/repositories/workspace_session_repository.rb +61 -0
- data/lib/prompt_objects/server/api/routes.rb +549 -53
- data/lib/prompt_objects/server/app.rb +133 -14
- data/lib/prompt_objects/server/file_watcher.rb +9 -0
- data/lib/prompt_objects/server/public/assets/index-BjKlODDD.js +90 -0
- data/lib/prompt_objects/server/public/assets/index-w3fpIb5c.css +1 -0
- data/lib/prompt_objects/server/public/index.html +2 -2
- data/lib/prompt_objects/server/websocket_handler.rb +202 -227
- data/lib/prompt_objects/server.rb +170 -1
- data/lib/prompt_objects/service.rb +129 -0
- data/lib/prompt_objects/session/store.rb +600 -13
- data/lib/prompt_objects/universal/add_capability.rb +1 -1
- data/lib/prompt_objects/universal/create_capability.rb +4 -2
- data/lib/prompt_objects/universal/delete_env_data.rb +23 -15
- data/lib/prompt_objects/universal/delete_primitive.rb +1 -1
- data/lib/prompt_objects/universal/dispatch_po.rb +54 -0
- data/lib/prompt_objects/universal/get_env_data.rb +8 -14
- data/lib/prompt_objects/universal/get_run_status.rb +30 -0
- data/lib/prompt_objects/universal/join_runs.rb +35 -0
- data/lib/prompt_objects/universal/list_env_data.rb +10 -14
- data/lib/prompt_objects/universal/modify_primitive.rb +1 -1
- data/lib/prompt_objects/universal/render_artifact.rb +91 -0
- data/lib/prompt_objects/universal/request_primitive.rb +1 -1
- data/lib/prompt_objects/universal/{store_env_data.rb → set_env_data.rb} +34 -20
- data/lib/prompt_objects/version.rb +5 -0
- data/lib/prompt_objects.rb +33 -8
- data/objects/coordinator.md +8 -4
- data/templates/arc-agi-1/objects/data_manager.md +2 -2
- data/templates/arc-agi-1/objects/observer.md +2 -2
- data/templates/arc-agi-1/objects/solver.md +6 -5
- data/templates/arc-agi-1/objects/verifier.md +2 -2
- data/templates/arc-agi-3/manifest.yml +19 -0
- data/templates/arc-agi-3/objects/analyst.md +79 -0
- data/templates/arc-agi-3/objects/player.md +76 -0
- data/templates/arc-agi-3/objects/strategist.md +97 -0
- data/templates/arc-agi-3/primitives/action_log.rb +133 -0
- data/templates/arc-agi-3/primitives/arc3_api.rb +223 -0
- data/templates/arc-agi-3/primitives/arc3_scorecard.rb +131 -0
- data/templates/arc-agi-3/primitives/frame_diff.rb +143 -0
- data/templates/arc-agi-3/primitives/frame_info.rb +96 -0
- data/templates/arc-agi-3/primitives/render_frame.rb +140 -0
- data/templates/basic/objects/basic.md +11 -3
- data/templates/chiptune/extras/sonic_pi_boot.rb +220 -0
- data/templates/chiptune/manifest.yml +20 -0
- data/templates/chiptune/objects/conductor.md +184 -0
- data/templates/chiptune/objects/sonic_pi.md +65 -0
- data/templates/chiptune/objects/voice_bass.md +44 -0
- data/templates/chiptune/objects/voice_lead.md +44 -0
- data/templates/chiptune/objects/voice_pad.md +43 -0
- data/templates/chiptune/objects/voice_perc.md +45 -0
- data/templates/chiptune/primitives/play_note.rb +59 -0
- data/templates/chiptune/primitives/play_sequence.rb +55 -0
- data/templates/chiptune/primitives/query_sonic_pi.rb +141 -0
- data/templates/chiptune/primitives/set_synth.rb +57 -0
- data/templates/chiptune/primitives/set_tempo.rb +45 -0
- data/templates/chiptune/primitives/stop_voice.rb +46 -0
- data/templates/chiptune/primitives/support/osc_bridge.rb +33 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/developer/objects/code_reviewer.md +1 -1
- data/templates/developer/objects/coordinator.md +16 -3
- data/templates/developer/objects/debugger.md +3 -3
- data/templates/empty/objects/assistant.md +10 -5
- data/templates/minimal/objects/assistant.md +10 -5
- data/templates/music/extras/sonic_pi_boot.rb +220 -0
- data/templates/music/manifest.yml +16 -0
- data/templates/music/objects/conductor.md +154 -0
- data/templates/music/objects/sonic_pi.md +65 -0
- data/templates/music/primitives/play_note.rb +55 -0
- data/templates/music/primitives/play_sequence.rb +51 -0
- data/templates/music/primitives/query_sonic_pi.rb +139 -0
- data/templates/music/primitives/set_synth.rb +53 -0
- data/templates/music/primitives/set_tempo.rb +43 -0
- data/templates/music/primitives/stop_voice.rb +44 -0
- data/templates/music/primitives/support/osc_bridge.rb +33 -0
- data/templates/music/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/music-beginner/README.md +100 -0
- data/templates/music-beginner/extras/README.md +27 -0
- data/templates/music-beginner/extras/sonic_pi_boot.rb +297 -0
- data/templates/music-beginner/manifest.yml +25 -0
- data/templates/music-beginner/objects/conductor.md +222 -0
- data/templates/music-beginner/objects/engine_setup.md +54 -0
- data/templates/music-beginner/objects/voice_bass.md +43 -0
- data/templates/music-beginner/objects/voice_keys.md +48 -0
- data/templates/music-beginner/objects/voice_pad.md +46 -0
- data/templates/music-beginner/objects/voice_percussion.md +45 -0
- data/templates/music-beginner/objects/voice_texture.md +37 -0
- data/templates/music-beginner/primitives/play_pattern.rb +118 -0
- data/templates/music-beginner/primitives/query_music_engine.rb +94 -0
- data/templates/music-beginner/primitives/set_instrument.rb +47 -0
- data/templates/music-beginner/primitives/set_tempo.rb +43 -0
- data/templates/music-beginner/primitives/set_voice_level.rb +46 -0
- data/templates/music-beginner/primitives/stop_voice.rb +37 -0
- data/templates/music-beginner/primitives/support/osc_bridge.rb +25 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/music-pro/extras/AUDITION.md +100 -0
- data/templates/music-pro/extras/sonic_pi_boot.rb +546 -0
- data/templates/music-pro/manifest.yml +19 -0
- data/templates/music-pro/objects/conductor.md +126 -0
- data/templates/music-pro/objects/sonic_pi.md +35 -0
- data/templates/music-pro/objects/voice_harmony.md +48 -0
- data/templates/music-pro/objects/voice_rhythm.md +43 -0
- data/templates/music-pro/objects/voice_texture.md +42 -0
- data/templates/music-pro/primitives/define_patch.rb +46 -0
- data/templates/music-pro/primitives/query_music_engine.rb +69 -0
- data/templates/music-pro/primitives/set_live_pattern.rb +70 -0
- data/templates/music-pro/primitives/set_tempo.rb +31 -0
- data/templates/music-pro/primitives/stop_voice.rb +31 -0
- data/templates/music-pro/primitives/support/music_protocol.rb +136 -0
- data/templates/music-pro/primitives/support/osc_bridge.rb +33 -0
- data/templates/music-pro/primitives/trigger_event.rb +44 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/writer/objects/coordinator.md +14 -3
- data/templates/writer/objects/editor.md +1 -1
- data/templates/writer/objects/researcher.md +2 -2
- data/tools/thread-explorer.html +1 -1
- metadata +172 -65
- data/CLAUDE.md +0 -177
- data/Gemfile +0 -10
- data/Rakefile +0 -27
- data/frontend/.gitignore +0 -3
- data/frontend/index.html +0 -17
- data/frontend/package-lock.json +0 -4540
- data/frontend/package.json +0 -36
- data/frontend/postcss.config.js +0 -6
- data/frontend/src/App.tsx +0 -104
- data/frontend/src/canvas/CanvasView.tsx +0 -113
- data/frontend/src/canvas/ForceLayout.ts +0 -115
- data/frontend/src/canvas/SceneManager.ts +0 -587
- data/frontend/src/canvas/canvasStore.ts +0 -47
- data/frontend/src/canvas/constants.ts +0 -95
- data/frontend/src/canvas/controls/CameraControls.ts +0 -98
- data/frontend/src/canvas/edges/MessageArc.ts +0 -149
- data/frontend/src/canvas/inspector/InspectorPanel.tsx +0 -31
- data/frontend/src/canvas/inspector/POInspector.tsx +0 -262
- data/frontend/src/canvas/inspector/ToolCallInspector.tsx +0 -67
- data/frontend/src/canvas/nodes/PONode.ts +0 -249
- data/frontend/src/canvas/nodes/ToolCallNode.ts +0 -116
- data/frontend/src/canvas/types.ts +0 -24
- data/frontend/src/components/ContextMenu.tsx +0 -68
- data/frontend/src/components/EnvDataPane.tsx +0 -69
- data/frontend/src/components/Inspector.tsx +0 -263
- data/frontend/src/components/MarkdownMessage.tsx +0 -155
- data/frontend/src/components/MethodList.tsx +0 -90
- data/frontend/src/components/ModelSelector.tsx +0 -115
- data/frontend/src/components/NotificationPanel.tsx +0 -130
- data/frontend/src/components/ObjectList.tsx +0 -78
- data/frontend/src/components/PaneSlot.tsx +0 -30
- data/frontend/src/components/SourcePane.tsx +0 -202
- data/frontend/src/components/SystemBar.tsx +0 -74
- data/frontend/src/components/Transcript.tsx +0 -76
- data/frontend/src/components/UsagePanel.tsx +0 -105
- data/frontend/src/components/Workspace.tsx +0 -260
- data/frontend/src/components/index.ts +0 -12
- data/frontend/src/hooks/useResize.ts +0 -55
- data/frontend/src/hooks/useWebSocket.ts +0 -559
- data/frontend/src/index.css +0 -102
- data/frontend/src/main.tsx +0 -10
- data/frontend/src/store/index.ts +0 -311
- data/frontend/src/types/index.ts +0 -176
- data/frontend/tailwind.config.js +0 -44
- data/frontend/tsconfig.json +0 -30
- data/frontend/vite.config.ts +0 -29
- data/lib/prompt_objects/server/public/assets/index-DEPawnfZ.js +0 -4345
- data/lib/prompt_objects/server/public/assets/index-oMrRce1m.css +0 -1
- data/lib/prompt_objects/universal/add_primitive.rb +0 -118
- data/lib/prompt_objects/universal/ask_human.rb +0 -109
- data/lib/prompt_objects/universal/create_primitive.rb +0 -174
- data/lib/prompt_objects/universal/list_primitives.rb +0 -145
- data/lib/prompt_objects/universal/think.rb +0 -41
- data/lib/prompt_objects/universal/update_env_data.rb +0 -88
- data/prompt_objects.gemspec +0 -50
- data/templates/basic/.gitignore +0 -2
- data/templates/developer/.gitignore +0 -5
- data/templates/empty/.gitignore +0 -5
- data/templates/empty/objects/.gitkeep +0 -0
- data/templates/minimal/.gitignore +0 -5
- data/templates/writer/.gitignore +0 -5
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
import { useState, useMemo } from 'react'
|
|
2
|
-
import { useStore, usePONotifications, useEnvData } from '../store'
|
|
3
|
-
import { useResize } from '../hooks/useResize'
|
|
4
|
-
import { MethodList } from './MethodList'
|
|
5
|
-
import { SourcePane } from './SourcePane'
|
|
6
|
-
import { EnvDataPane } from './EnvDataPane'
|
|
7
|
-
import { Workspace } from './Workspace'
|
|
8
|
-
import { ContextMenu } from './ContextMenu'
|
|
9
|
-
import { PaneSlot } from './PaneSlot'
|
|
10
|
-
import type { PromptObject, CapabilityInfo } from '../types'
|
|
11
|
-
|
|
12
|
-
interface InspectorProps {
|
|
13
|
-
po: PromptObject
|
|
14
|
-
sendMessage: (target: string, content: string, newThread?: boolean) => void
|
|
15
|
-
createSession?: (target: string, name?: string) => void
|
|
16
|
-
switchSession: (target: string, sessionId: string) => void
|
|
17
|
-
createThread: (target: string) => void
|
|
18
|
-
updatePrompt: (target: string, prompt: string) => void
|
|
19
|
-
requestUsage?: (sessionId: string, includeTree?: boolean) => void
|
|
20
|
-
exportThread?: (sessionId: string, format?: string) => void
|
|
21
|
-
requestEnvData: (sessionId: string) => void
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function Inspector({
|
|
25
|
-
po,
|
|
26
|
-
sendMessage,
|
|
27
|
-
switchSession,
|
|
28
|
-
createThread,
|
|
29
|
-
updatePrompt,
|
|
30
|
-
requestUsage,
|
|
31
|
-
exportThread,
|
|
32
|
-
requestEnvData,
|
|
33
|
-
}: InspectorProps) {
|
|
34
|
-
const [selectedCapability, setSelectedCapability] = useState<CapabilityInfo | null>(null)
|
|
35
|
-
const [threadMenuOpen, setThreadMenuOpen] = useState(false)
|
|
36
|
-
const [contextMenu, setContextMenu] = useState<{ x: number; y: number; sessionId: string } | null>(null)
|
|
37
|
-
const notifications = usePONotifications(po.name)
|
|
38
|
-
const topPaneCollapsed = useStore((s) => s.topPaneCollapsed)
|
|
39
|
-
const toggleTopPane = useStore((s) => s.toggleTopPane)
|
|
40
|
-
const envDataPaneCollapsed = useStore((s) => s.envDataPaneCollapsed)
|
|
41
|
-
const toggleEnvDataPane = useStore((s) => s.toggleEnvDataPane)
|
|
42
|
-
|
|
43
|
-
const topPaneResize = useResize({
|
|
44
|
-
direction: 'vertical',
|
|
45
|
-
initialSize: 260,
|
|
46
|
-
minSize: 120,
|
|
47
|
-
maxSize: 600,
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
const envDataResize = useResize({
|
|
51
|
-
direction: 'vertical',
|
|
52
|
-
initialSize: 160,
|
|
53
|
-
minSize: 80,
|
|
54
|
-
maxSize: 400,
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
const methodListResize = useResize({
|
|
58
|
-
direction: 'horizontal',
|
|
59
|
-
initialSize: 192,
|
|
60
|
-
minSize: 120,
|
|
61
|
-
maxSize: 320,
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
const sessions = po.sessions || []
|
|
65
|
-
const currentSessionId = po.current_session?.id
|
|
66
|
-
const sessionRootMap = useStore((s) => s.sessionRootMap)
|
|
67
|
-
const rootThreadId = currentSessionId ? sessionRootMap[currentSessionId] : undefined
|
|
68
|
-
const envDataEntries = useEnvData(rootThreadId)
|
|
69
|
-
|
|
70
|
-
// Sort sessions: current first, then by updated_at desc
|
|
71
|
-
const sortedSessions = useMemo(() => {
|
|
72
|
-
return [...sessions].sort((a, b) => {
|
|
73
|
-
if (a.id === currentSessionId) return -1
|
|
74
|
-
if (b.id === currentSessionId) return 1
|
|
75
|
-
return (b.updated_at || '').localeCompare(a.updated_at || '')
|
|
76
|
-
})
|
|
77
|
-
}, [sessions, currentSessionId])
|
|
78
|
-
|
|
79
|
-
const isActive = po.status !== 'idle'
|
|
80
|
-
|
|
81
|
-
const statusDot = {
|
|
82
|
-
idle: 'bg-po-status-idle',
|
|
83
|
-
thinking: 'bg-po-status-active',
|
|
84
|
-
calling_tool: 'bg-po-status-calling',
|
|
85
|
-
}[po.status] || 'bg-po-status-idle'
|
|
86
|
-
|
|
87
|
-
const statusGlow = {
|
|
88
|
-
idle: '',
|
|
89
|
-
thinking: 'shadow-[0_0_6px_rgba(212,149,42,0.7)]',
|
|
90
|
-
calling_tool: 'shadow-[0_0_6px_rgba(59,154,110,0.7)]',
|
|
91
|
-
}[po.status] || ''
|
|
92
|
-
|
|
93
|
-
const statusLabelColor = {
|
|
94
|
-
idle: 'text-po-text-ghost',
|
|
95
|
-
thinking: 'text-po-status-active',
|
|
96
|
-
calling_tool: 'text-po-status-calling',
|
|
97
|
-
}[po.status] || 'text-po-text-ghost'
|
|
98
|
-
|
|
99
|
-
const statusLabel = {
|
|
100
|
-
idle: 'idle',
|
|
101
|
-
thinking: 'thinking...',
|
|
102
|
-
calling_tool: 'calling tool...',
|
|
103
|
-
}[po.status] || po.status
|
|
104
|
-
|
|
105
|
-
const handleThreadContextMenu = (e: React.MouseEvent, sessionId: string) => {
|
|
106
|
-
e.preventDefault()
|
|
107
|
-
setContextMenu({ x: e.clientX, y: e.clientY, sessionId })
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return (
|
|
111
|
-
<div className="h-full flex flex-col">
|
|
112
|
-
{/* Inspector Header */}
|
|
113
|
-
<div className="h-8 bg-po-surface-2 border-b border-po-border flex items-center px-3 gap-2 flex-shrink-0">
|
|
114
|
-
<div className="relative flex-shrink-0">
|
|
115
|
-
<div className={`w-2 h-2 rounded-full ${statusDot} ${statusGlow} ${isActive ? 'animate-pulse' : ''}`} />
|
|
116
|
-
</div>
|
|
117
|
-
<span className="font-mono text-xs text-po-text-primary font-medium">{po.name}</span>
|
|
118
|
-
<span className={`text-2xs font-medium truncate ${statusLabelColor} ${isActive ? 'animate-pulse' : ''}`}>{statusLabel}</span>
|
|
119
|
-
{po.description && (
|
|
120
|
-
<span className="text-2xs text-po-text-ghost truncate hidden sm:inline">{po.description}</span>
|
|
121
|
-
)}
|
|
122
|
-
{notifications.length > 0 && (
|
|
123
|
-
<span className="text-2xs font-mono bg-po-warning text-po-bg px-1 rounded font-bold">
|
|
124
|
-
{notifications.length}
|
|
125
|
-
</span>
|
|
126
|
-
)}
|
|
127
|
-
|
|
128
|
-
<div className="flex-1" />
|
|
129
|
-
|
|
130
|
-
{/* Thread picker */}
|
|
131
|
-
<div className="relative">
|
|
132
|
-
<button
|
|
133
|
-
onClick={() => setThreadMenuOpen(!threadMenuOpen)}
|
|
134
|
-
className="flex items-center gap-1 text-2xs text-po-text-secondary hover:text-po-text-primary transition-colors duration-150"
|
|
135
|
-
>
|
|
136
|
-
<span className="font-mono">
|
|
137
|
-
{currentSessionId
|
|
138
|
-
? sessions.find(s => s.id === currentSessionId)?.name || `Thread ${currentSessionId.slice(0, 6)}`
|
|
139
|
-
: 'No thread'}
|
|
140
|
-
</span>
|
|
141
|
-
<svg className={`w-3 h-3 transition-transform ${threadMenuOpen ? 'rotate-180' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
142
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
143
|
-
</svg>
|
|
144
|
-
</button>
|
|
145
|
-
|
|
146
|
-
{threadMenuOpen && (
|
|
147
|
-
<div className="absolute right-0 top-full mt-1 w-56 bg-po-surface-2 border border-po-border rounded shadow-xl z-50 overflow-hidden">
|
|
148
|
-
<button
|
|
149
|
-
onClick={() => { createThread(po.name); setThreadMenuOpen(false) }}
|
|
150
|
-
className="w-full text-left px-2.5 py-1.5 text-xs text-po-accent hover:bg-po-surface-3 transition-colors duration-150 border-b border-po-border"
|
|
151
|
-
>
|
|
152
|
-
+ New Thread
|
|
153
|
-
</button>
|
|
154
|
-
<div className="max-h-48 overflow-auto">
|
|
155
|
-
{sortedSessions.map((session) => (
|
|
156
|
-
<button
|
|
157
|
-
key={session.id}
|
|
158
|
-
onClick={() => { switchSession(po.name, session.id); setThreadMenuOpen(false) }}
|
|
159
|
-
onContextMenu={(e) => handleThreadContextMenu(e, session.id)}
|
|
160
|
-
className={`w-full text-left px-2.5 py-1.5 text-xs transition-colors duration-150 ${
|
|
161
|
-
session.id === currentSessionId
|
|
162
|
-
? 'bg-po-accent-wash text-po-accent'
|
|
163
|
-
: 'text-po-text-secondary hover:bg-po-surface-3'
|
|
164
|
-
}`}
|
|
165
|
-
>
|
|
166
|
-
<div className="flex items-center gap-1.5">
|
|
167
|
-
{session.thread_type === 'delegation' && <span className="text-po-status-delegated">↳</span>}
|
|
168
|
-
<span className="font-mono truncate flex-1">
|
|
169
|
-
{session.name || `Thread ${session.id.slice(0, 6)}`}
|
|
170
|
-
</span>
|
|
171
|
-
<span className="text-2xs text-po-text-ghost">{session.message_count}m</span>
|
|
172
|
-
</div>
|
|
173
|
-
{session.parent_po && (
|
|
174
|
-
<div className="text-2xs text-po-status-delegated mt-0.5">from {session.parent_po}</div>
|
|
175
|
-
)}
|
|
176
|
-
</button>
|
|
177
|
-
))}
|
|
178
|
-
</div>
|
|
179
|
-
</div>
|
|
180
|
-
)}
|
|
181
|
-
</div>
|
|
182
|
-
</div>
|
|
183
|
-
|
|
184
|
-
{/* Top: Methods + Source (collapsible, resizable height) */}
|
|
185
|
-
<PaneSlot
|
|
186
|
-
label="Methods | Source"
|
|
187
|
-
collapsed={topPaneCollapsed}
|
|
188
|
-
onToggle={toggleTopPane}
|
|
189
|
-
height={topPaneResize.size}
|
|
190
|
-
resizeHandle={
|
|
191
|
-
<div
|
|
192
|
-
className="resize-handle-h"
|
|
193
|
-
onMouseDown={topPaneResize.onMouseDown}
|
|
194
|
-
/>
|
|
195
|
-
}
|
|
196
|
-
>
|
|
197
|
-
<div className="flex h-full">
|
|
198
|
-
{/* Method List (resizable width) */}
|
|
199
|
-
<div style={{ width: methodListResize.size }} className="flex-shrink-0">
|
|
200
|
-
<MethodList
|
|
201
|
-
po={po}
|
|
202
|
-
selectedCapability={selectedCapability}
|
|
203
|
-
onSelectCapability={setSelectedCapability}
|
|
204
|
-
/>
|
|
205
|
-
</div>
|
|
206
|
-
|
|
207
|
-
{/* Resize handle */}
|
|
208
|
-
<div
|
|
209
|
-
className="resize-handle"
|
|
210
|
-
onMouseDown={methodListResize.onMouseDown}
|
|
211
|
-
/>
|
|
212
|
-
|
|
213
|
-
{/* Source Pane */}
|
|
214
|
-
<SourcePane
|
|
215
|
-
po={po}
|
|
216
|
-
selectedCapability={selectedCapability}
|
|
217
|
-
onSave={(prompt) => updatePrompt(po.name, prompt)}
|
|
218
|
-
/>
|
|
219
|
-
</div>
|
|
220
|
-
</PaneSlot>
|
|
221
|
-
|
|
222
|
-
{/* Middle: Env Data (collapsible, resizable height) */}
|
|
223
|
-
<PaneSlot
|
|
224
|
-
label={`Env Data${envDataEntries.length > 0 ? ` (${envDataEntries.length})` : ''}`}
|
|
225
|
-
collapsed={envDataPaneCollapsed}
|
|
226
|
-
onToggle={toggleEnvDataPane}
|
|
227
|
-
height={envDataResize.size}
|
|
228
|
-
resizeHandle={
|
|
229
|
-
<div
|
|
230
|
-
className="resize-handle-h"
|
|
231
|
-
onMouseDown={envDataResize.onMouseDown}
|
|
232
|
-
/>
|
|
233
|
-
}
|
|
234
|
-
>
|
|
235
|
-
<EnvDataPane sessionId={currentSessionId} requestEnvData={requestEnvData} />
|
|
236
|
-
</PaneSlot>
|
|
237
|
-
|
|
238
|
-
{/* Bottom: Workspace */}
|
|
239
|
-
<div className="flex-1 overflow-hidden">
|
|
240
|
-
<Workspace po={po} sendMessage={sendMessage} />
|
|
241
|
-
</div>
|
|
242
|
-
|
|
243
|
-
{/* Context menu for thread right-click */}
|
|
244
|
-
{contextMenu && (
|
|
245
|
-
<ContextMenu
|
|
246
|
-
x={contextMenu.x}
|
|
247
|
-
y={contextMenu.y}
|
|
248
|
-
onClose={() => setContextMenu(null)}
|
|
249
|
-
items={[
|
|
250
|
-
...(requestUsage ? [
|
|
251
|
-
{ label: 'View Usage', onClick: () => requestUsage(contextMenu.sessionId) },
|
|
252
|
-
{ label: 'View Tree Usage', onClick: () => requestUsage(contextMenu.sessionId, true) },
|
|
253
|
-
] : []),
|
|
254
|
-
...(exportThread ? [
|
|
255
|
-
{ label: 'Export Markdown', onClick: () => exportThread(contextMenu.sessionId, 'markdown') },
|
|
256
|
-
{ label: 'Export JSON', onClick: () => exportThread(contextMenu.sessionId, 'json') },
|
|
257
|
-
] : []),
|
|
258
|
-
]}
|
|
259
|
-
/>
|
|
260
|
-
)}
|
|
261
|
-
</div>
|
|
262
|
-
)
|
|
263
|
-
}
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import ReactMarkdown from 'react-markdown'
|
|
2
|
-
import remarkGfm from 'remark-gfm'
|
|
3
|
-
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
|
|
4
|
-
import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
|
|
5
|
-
import { useState } from 'react'
|
|
6
|
-
|
|
7
|
-
interface MarkdownMessageProps {
|
|
8
|
-
content: string
|
|
9
|
-
className?: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function MarkdownMessage({ content, className = '' }: MarkdownMessageProps) {
|
|
13
|
-
return (
|
|
14
|
-
<div className={`markdown-content ${className}`}>
|
|
15
|
-
<ReactMarkdown
|
|
16
|
-
remarkPlugins={[remarkGfm]}
|
|
17
|
-
components={{
|
|
18
|
-
// Code blocks with syntax highlighting
|
|
19
|
-
code({ className, children, ...props }) {
|
|
20
|
-
const match = /language-(\w+)/.exec(className || '')
|
|
21
|
-
const language = match ? match[1] : ''
|
|
22
|
-
const codeString = String(children).replace(/\n$/, '')
|
|
23
|
-
const isBlock = codeString.includes('\n') || match
|
|
24
|
-
|
|
25
|
-
if (isBlock) {
|
|
26
|
-
return (
|
|
27
|
-
<CodeBlock language={language} code={codeString} />
|
|
28
|
-
)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<code
|
|
33
|
-
className="bg-po-surface-2 px-1 py-0.5 rounded text-po-accent font-mono text-[0.9em]"
|
|
34
|
-
{...props}
|
|
35
|
-
>
|
|
36
|
-
{children}
|
|
37
|
-
</code>
|
|
38
|
-
)
|
|
39
|
-
},
|
|
40
|
-
// Styled links
|
|
41
|
-
a({ href, children }) {
|
|
42
|
-
return (
|
|
43
|
-
<a
|
|
44
|
-
href={href}
|
|
45
|
-
target="_blank"
|
|
46
|
-
rel="noopener noreferrer"
|
|
47
|
-
className="text-po-accent hover:underline"
|
|
48
|
-
>
|
|
49
|
-
{children}
|
|
50
|
-
</a>
|
|
51
|
-
)
|
|
52
|
-
},
|
|
53
|
-
// Styled paragraphs
|
|
54
|
-
p({ children }) {
|
|
55
|
-
return <p className="mb-2 last:mb-0">{children}</p>
|
|
56
|
-
},
|
|
57
|
-
// Styled lists
|
|
58
|
-
ul({ children }) {
|
|
59
|
-
return <ul className="list-disc list-inside mb-2 space-y-0.5">{children}</ul>
|
|
60
|
-
},
|
|
61
|
-
ol({ children }) {
|
|
62
|
-
return <ol className="list-decimal list-inside mb-2 space-y-0.5">{children}</ol>
|
|
63
|
-
},
|
|
64
|
-
// Styled headings
|
|
65
|
-
h1({ children }) {
|
|
66
|
-
return <h1 className="text-base font-bold mb-1.5 mt-3 first:mt-0 text-po-text-primary">{children}</h1>
|
|
67
|
-
},
|
|
68
|
-
h2({ children }) {
|
|
69
|
-
return <h2 className="text-sm font-bold mb-1.5 mt-2 first:mt-0 text-po-text-primary">{children}</h2>
|
|
70
|
-
},
|
|
71
|
-
h3({ children }) {
|
|
72
|
-
return <h3 className="text-xs font-bold mb-1 mt-1.5 first:mt-0 text-po-text-primary">{children}</h3>
|
|
73
|
-
},
|
|
74
|
-
// Styled blockquotes
|
|
75
|
-
blockquote({ children }) {
|
|
76
|
-
return (
|
|
77
|
-
<blockquote className="border-l-2 border-po-accent pl-3 my-2 text-po-text-secondary italic">
|
|
78
|
-
{children}
|
|
79
|
-
</blockquote>
|
|
80
|
-
)
|
|
81
|
-
},
|
|
82
|
-
// Styled tables
|
|
83
|
-
table({ children }) {
|
|
84
|
-
return (
|
|
85
|
-
<div className="overflow-x-auto my-2">
|
|
86
|
-
<table className="min-w-full border border-po-border text-xs">{children}</table>
|
|
87
|
-
</div>
|
|
88
|
-
)
|
|
89
|
-
},
|
|
90
|
-
th({ children }) {
|
|
91
|
-
return (
|
|
92
|
-
<th className="border border-po-border bg-po-surface-2 px-2 py-1 text-left font-medium text-po-text-primary">
|
|
93
|
-
{children}
|
|
94
|
-
</th>
|
|
95
|
-
)
|
|
96
|
-
},
|
|
97
|
-
td({ children }) {
|
|
98
|
-
return (
|
|
99
|
-
<td className="border border-po-border px-2 py-1 text-po-text-secondary">{children}</td>
|
|
100
|
-
)
|
|
101
|
-
},
|
|
102
|
-
// Horizontal rule
|
|
103
|
-
hr() {
|
|
104
|
-
return <hr className="border-po-border my-3" />
|
|
105
|
-
},
|
|
106
|
-
}}
|
|
107
|
-
>
|
|
108
|
-
{content}
|
|
109
|
-
</ReactMarkdown>
|
|
110
|
-
</div>
|
|
111
|
-
)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function CodeBlock({ language, code }: { language: string; code: string }) {
|
|
115
|
-
const [copied, setCopied] = useState(false)
|
|
116
|
-
|
|
117
|
-
const handleCopy = async () => {
|
|
118
|
-
await navigator.clipboard.writeText(code)
|
|
119
|
-
setCopied(true)
|
|
120
|
-
setTimeout(() => setCopied(false), 2000)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return (
|
|
124
|
-
<div className="relative group my-2">
|
|
125
|
-
{/* Language label and copy button */}
|
|
126
|
-
<div className="flex items-center justify-between bg-po-surface-2 px-2.5 py-1 rounded-t border border-b-0 border-po-border">
|
|
127
|
-
<span className="text-2xs text-po-text-ghost font-mono">
|
|
128
|
-
{language || 'text'}
|
|
129
|
-
</span>
|
|
130
|
-
<button
|
|
131
|
-
onClick={handleCopy}
|
|
132
|
-
className="text-2xs text-po-text-ghost hover:text-po-text-primary transition-colors duration-150"
|
|
133
|
-
>
|
|
134
|
-
{copied ? 'Copied' : 'Copy'}
|
|
135
|
-
</button>
|
|
136
|
-
</div>
|
|
137
|
-
{/* Code with syntax highlighting */}
|
|
138
|
-
<SyntaxHighlighter
|
|
139
|
-
style={oneDark}
|
|
140
|
-
language={language || 'text'}
|
|
141
|
-
PreTag="div"
|
|
142
|
-
customStyle={{
|
|
143
|
-
margin: 0,
|
|
144
|
-
borderRadius: '0 0 0.25rem 0.25rem',
|
|
145
|
-
border: '1px solid #3d3a37',
|
|
146
|
-
borderTop: 'none',
|
|
147
|
-
fontSize: '11px',
|
|
148
|
-
background: '#222120',
|
|
149
|
-
}}
|
|
150
|
-
>
|
|
151
|
-
{code}
|
|
152
|
-
</SyntaxHighlighter>
|
|
153
|
-
</div>
|
|
154
|
-
)
|
|
155
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import type { PromptObject, CapabilityInfo } from '../types'
|
|
2
|
-
|
|
3
|
-
interface MethodListProps {
|
|
4
|
-
po: PromptObject
|
|
5
|
-
selectedCapability: CapabilityInfo | null
|
|
6
|
-
onSelectCapability: (cap: CapabilityInfo | null) => void
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function MethodList({ po, selectedCapability, onSelectCapability }: MethodListProps) {
|
|
10
|
-
const capabilities = po.capabilities || []
|
|
11
|
-
const universalCapabilities = po.universal_capabilities || []
|
|
12
|
-
|
|
13
|
-
const handleClick = (cap: CapabilityInfo) => {
|
|
14
|
-
if (selectedCapability?.name === cap.name) {
|
|
15
|
-
onSelectCapability(null) // Toggle off
|
|
16
|
-
} else {
|
|
17
|
-
onSelectCapability(cap)
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<div className="h-full border-r border-po-border overflow-auto bg-po-surface">
|
|
23
|
-
{/* Source (prompt view) */}
|
|
24
|
-
<button
|
|
25
|
-
onClick={() => onSelectCapability(null)}
|
|
26
|
-
className={`w-full text-left px-2.5 py-1 text-xs font-mono border-b border-po-border transition-colors duration-150 ${
|
|
27
|
-
selectedCapability === null
|
|
28
|
-
? 'bg-po-accent-wash text-po-accent'
|
|
29
|
-
: 'text-po-text-secondary hover:bg-po-surface-2'
|
|
30
|
-
}`}
|
|
31
|
-
>
|
|
32
|
-
Source
|
|
33
|
-
</button>
|
|
34
|
-
|
|
35
|
-
{/* Declared capabilities */}
|
|
36
|
-
{capabilities.length > 0 && (
|
|
37
|
-
<div>
|
|
38
|
-
<div className="px-2.5 py-1.5 border-b border-po-border">
|
|
39
|
-
<span className="text-2xs font-medium text-po-text-ghost uppercase tracking-wider">
|
|
40
|
-
Methods ({capabilities.length})
|
|
41
|
-
</span>
|
|
42
|
-
</div>
|
|
43
|
-
{capabilities.map((cap) => (
|
|
44
|
-
<button
|
|
45
|
-
key={cap.name}
|
|
46
|
-
onClick={() => handleClick(cap)}
|
|
47
|
-
className={`w-full text-left px-2.5 py-1 text-xs font-mono transition-colors duration-150 ${
|
|
48
|
-
selectedCapability?.name === cap.name
|
|
49
|
-
? 'bg-po-accent-wash text-po-accent'
|
|
50
|
-
: 'text-po-accent hover:bg-po-surface-2'
|
|
51
|
-
}`}
|
|
52
|
-
>
|
|
53
|
-
{cap.name}
|
|
54
|
-
</button>
|
|
55
|
-
))}
|
|
56
|
-
</div>
|
|
57
|
-
)}
|
|
58
|
-
|
|
59
|
-
{/* Universal capabilities */}
|
|
60
|
-
{universalCapabilities.length > 0 && (
|
|
61
|
-
<div>
|
|
62
|
-
<div className="px-2.5 py-1.5 border-b border-po-border border-t border-po-border">
|
|
63
|
-
<span className="text-2xs font-medium text-po-text-ghost uppercase tracking-wider">
|
|
64
|
-
Universal ({universalCapabilities.length})
|
|
65
|
-
</span>
|
|
66
|
-
</div>
|
|
67
|
-
{universalCapabilities.map((cap) => (
|
|
68
|
-
<button
|
|
69
|
-
key={cap.name}
|
|
70
|
-
onClick={() => handleClick(cap)}
|
|
71
|
-
className={`w-full text-left px-2.5 py-1 text-xs font-mono transition-colors duration-150 ${
|
|
72
|
-
selectedCapability?.name === cap.name
|
|
73
|
-
? 'bg-po-accent-wash text-po-text-secondary'
|
|
74
|
-
: 'text-po-text-secondary hover:bg-po-surface-2'
|
|
75
|
-
}`}
|
|
76
|
-
>
|
|
77
|
-
{cap.name}
|
|
78
|
-
</button>
|
|
79
|
-
))}
|
|
80
|
-
</div>
|
|
81
|
-
)}
|
|
82
|
-
|
|
83
|
-
{capabilities.length === 0 && universalCapabilities.length === 0 && (
|
|
84
|
-
<div className="px-2.5 py-4 text-2xs text-po-text-ghost text-center">
|
|
85
|
-
No capabilities
|
|
86
|
-
</div>
|
|
87
|
-
)}
|
|
88
|
-
</div>
|
|
89
|
-
)
|
|
90
|
-
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { useState, useRef, useEffect } from 'react'
|
|
2
|
-
import { useStore } from '../store'
|
|
3
|
-
|
|
4
|
-
interface Props {
|
|
5
|
-
switchLLM: (provider: string, model?: string) => void
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function ModelSelector({ switchLLM }: Props) {
|
|
9
|
-
const { llmConfig } = useStore()
|
|
10
|
-
const [isOpen, setIsOpen] = useState(false)
|
|
11
|
-
const dropdownRef = useRef<HTMLDivElement>(null)
|
|
12
|
-
|
|
13
|
-
// Close dropdown when clicking outside
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
function handleClickOutside(event: MouseEvent) {
|
|
16
|
-
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
|
17
|
-
setIsOpen(false)
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
document.addEventListener('mousedown', handleClickOutside)
|
|
21
|
-
return () => document.removeEventListener('mousedown', handleClickOutside)
|
|
22
|
-
}, [])
|
|
23
|
-
|
|
24
|
-
if (!llmConfig) return null
|
|
25
|
-
|
|
26
|
-
const handleSelectModel = (provider: string, model: string) => {
|
|
27
|
-
switchLLM(provider, model)
|
|
28
|
-
setIsOpen(false)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const providerNames: Record<string, string> = {
|
|
32
|
-
openai: 'OpenAI',
|
|
33
|
-
anthropic: 'Anthropic',
|
|
34
|
-
gemini: 'Gemini',
|
|
35
|
-
ollama: 'Ollama',
|
|
36
|
-
openrouter: 'OpenRouter',
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<div className="relative" ref={dropdownRef}>
|
|
41
|
-
<button
|
|
42
|
-
onClick={() => setIsOpen(!isOpen)}
|
|
43
|
-
className="flex items-center gap-1.5 px-2 py-0.5 text-xs bg-po-surface-2 border border-po-border rounded hover:border-po-border-focus transition-colors duration-150"
|
|
44
|
-
>
|
|
45
|
-
<span className="text-po-text-tertiary">
|
|
46
|
-
{providerNames[llmConfig.current_provider] || llmConfig.current_provider}
|
|
47
|
-
</span>
|
|
48
|
-
<span className="font-mono text-po-text-primary">{llmConfig.current_model}</span>
|
|
49
|
-
<svg
|
|
50
|
-
className={`w-3 h-3 text-po-text-ghost transition-transform ${isOpen ? 'rotate-180' : ''}`}
|
|
51
|
-
fill="none"
|
|
52
|
-
stroke="currentColor"
|
|
53
|
-
viewBox="0 0 24 24"
|
|
54
|
-
>
|
|
55
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
56
|
-
</svg>
|
|
57
|
-
</button>
|
|
58
|
-
|
|
59
|
-
{isOpen && (
|
|
60
|
-
<div className="absolute right-0 top-full mt-1 w-60 bg-po-surface-2 border border-po-border rounded shadow-xl z-50 overflow-hidden">
|
|
61
|
-
{llmConfig.providers.map((provider) => (
|
|
62
|
-
<div key={provider.name}>
|
|
63
|
-
<div className="px-2.5 py-1.5 bg-po-surface text-2xs font-medium text-po-text-ghost uppercase tracking-wider flex items-center justify-between">
|
|
64
|
-
<span>{providerNames[provider.name] || provider.name}</span>
|
|
65
|
-
{!provider.available && (
|
|
66
|
-
<span className="text-po-error text-2xs normal-case">
|
|
67
|
-
{provider.name === 'ollama' ? 'Not Running' : 'No API Key'}
|
|
68
|
-
</span>
|
|
69
|
-
)}
|
|
70
|
-
</div>
|
|
71
|
-
{provider.models.map((model) => {
|
|
72
|
-
const isSelected =
|
|
73
|
-
provider.name === llmConfig.current_provider &&
|
|
74
|
-
model === llmConfig.current_model
|
|
75
|
-
const isAvailable = provider.available
|
|
76
|
-
const isDefault = model === provider.default_model
|
|
77
|
-
|
|
78
|
-
return (
|
|
79
|
-
<button
|
|
80
|
-
key={`${provider.name}-${model}`}
|
|
81
|
-
onClick={() => isAvailable && handleSelectModel(provider.name, model)}
|
|
82
|
-
disabled={!isAvailable}
|
|
83
|
-
className={`w-full px-2.5 py-1.5 text-left text-xs font-mono flex items-center justify-between transition-colors duration-150 ${
|
|
84
|
-
isSelected
|
|
85
|
-
? 'bg-po-accent-wash text-po-accent'
|
|
86
|
-
: isAvailable
|
|
87
|
-
? 'text-po-text-secondary hover:bg-po-surface-3'
|
|
88
|
-
: 'text-po-text-ghost cursor-not-allowed'
|
|
89
|
-
}`}
|
|
90
|
-
>
|
|
91
|
-
<span className="flex items-center gap-2">
|
|
92
|
-
{model}
|
|
93
|
-
{isDefault && (
|
|
94
|
-
<span className="text-2xs text-po-text-ghost">(default)</span>
|
|
95
|
-
)}
|
|
96
|
-
</span>
|
|
97
|
-
{isSelected && (
|
|
98
|
-
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
|
99
|
-
<path
|
|
100
|
-
fillRule="evenodd"
|
|
101
|
-
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
|
102
|
-
clipRule="evenodd"
|
|
103
|
-
/>
|
|
104
|
-
</svg>
|
|
105
|
-
)}
|
|
106
|
-
</button>
|
|
107
|
-
)
|
|
108
|
-
})}
|
|
109
|
-
</div>
|
|
110
|
-
))}
|
|
111
|
-
</div>
|
|
112
|
-
)}
|
|
113
|
-
</div>
|
|
114
|
-
)
|
|
115
|
-
}
|