@assistant-ui/mcp-docs-server 0.2.3 → 0.3.1
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/README.md +16 -43
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -91
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +9 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +91 -0
- package/dist/proxy.js.map +1 -0
- package/package.json +8 -12
- package/src/index.ts +3 -115
- package/src/proxy.test.ts +385 -0
- package/src/proxy.ts +126 -0
- package/.docs/organized/code-examples/waterfall.md +0 -807
- package/.docs/organized/code-examples/with-a2a.md +0 -675
- package/.docs/organized/code-examples/with-ag-ui.md +0 -553
- package/.docs/organized/code-examples/with-ai-sdk-v7.md +0 -466
- package/.docs/organized/code-examples/with-artifacts.md +0 -815
- package/.docs/organized/code-examples/with-assistant-transport.md +0 -576
- package/.docs/organized/code-examples/with-browser-extension.md +0 -371
- package/.docs/organized/code-examples/with-chain-of-thought.md +0 -962
- package/.docs/organized/code-examples/with-cloud-standalone.md +0 -681
- package/.docs/organized/code-examples/with-cloud.md +0 -439
- package/.docs/organized/code-examples/with-custom-thread-list.md +0 -569
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +0 -527
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +0 -697
- package/.docs/organized/code-examples/with-eve.md +0 -405
- package/.docs/organized/code-examples/with-expo.md +0 -2246
- package/.docs/organized/code-examples/with-external-store.md +0 -421
- package/.docs/organized/code-examples/with-ffmpeg.md +0 -829
- package/.docs/organized/code-examples/with-generative-ui.md +0 -1810
- package/.docs/organized/code-examples/with-google-adk.md +0 -368
- package/.docs/organized/code-examples/with-heat-graph.md +0 -304
- package/.docs/organized/code-examples/with-image-generation.md +0 -463
- package/.docs/organized/code-examples/with-interactables.md +0 -733
- package/.docs/organized/code-examples/with-langchain.md +0 -446
- package/.docs/organized/code-examples/with-langgraph.md +0 -855
- package/.docs/organized/code-examples/with-livekit.md +0 -643
- package/.docs/organized/code-examples/with-mcp.md +0 -782
- package/.docs/organized/code-examples/with-nuxt.md +0 -2428
- package/.docs/organized/code-examples/with-opencode.md +0 -1974
- package/.docs/organized/code-examples/with-openui.md +0 -450
- package/.docs/organized/code-examples/with-pi.md +0 -2084
- package/.docs/organized/code-examples/with-react-hook-form.md +0 -727
- package/.docs/organized/code-examples/with-react-ink-web.md +0 -740
- package/.docs/organized/code-examples/with-react-ink.md +0 -473
- package/.docs/organized/code-examples/with-react-router.md +0 -936
- package/.docs/organized/code-examples/with-resumable-stream.md +0 -668
- package/.docs/organized/code-examples/with-store.md +0 -679
- package/.docs/organized/code-examples/with-svelte.md +0 -415
- package/.docs/organized/code-examples/with-sveltekit.md +0 -1061
- package/.docs/organized/code-examples/with-tanstack.md +0 -812
- package/.docs/organized/code-examples/with-tap-runtime.md +0 -813
- package/.docs/organized/code-examples/with-virtualized-thread.md +0 -681
- package/.docs/organized/code-examples/with-vue.md +0 -408
- package/.docs/raw/docs/(getting-started)/architecture.mdx +0 -146
- package/.docs/raw/docs/(getting-started)/base-ui.mdx +0 -39
- package/.docs/raw/docs/(getting-started)/cli.mdx +0 -542
- package/.docs/raw/docs/(getting-started)/devtools.mdx +0 -79
- package/.docs/raw/docs/(getting-started)/index.mdx +0 -23
- package/.docs/raw/docs/(getting-started)/installation.mdx +0 -483
- package/.docs/raw/docs/(getting-started)/llm.mdx +0 -211
- package/.docs/raw/docs/(getting-started)/rtl.mdx +0 -78
- package/.docs/raw/docs/(reference)/api-reference/adapters/attachments.mdx +0 -36
- package/.docs/raw/docs/(reference)/api-reference/adapters/feedback.mdx +0 -20
- package/.docs/raw/docs/(reference)/api-reference/adapters/index.mdx +0 -34
- package/.docs/raw/docs/(reference)/api-reference/adapters/model.mdx +0 -44
- package/.docs/raw/docs/(reference)/api-reference/adapters/persistence.mdx +0 -59
- package/.docs/raw/docs/(reference)/api-reference/adapters/runtime.mdx +0 -20
- package/.docs/raw/docs/(reference)/api-reference/adapters/suggestions.mdx +0 -26
- package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +0 -84
- package/.docs/raw/docs/(reference)/api-reference/context-providers/index.mdx +0 -22
- package/.docs/raw/docs/(reference)/api-reference/context-providers/scoped-providers.mdx +0 -64
- package/.docs/raw/docs/(reference)/api-reference/external-store/index.mdx +0 -22
- package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +0 -57
- package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +0 -49
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/a2ui.mdx +0 -40
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/actions.mdx +0 -56
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/components.mdx +0 -86
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +0 -39
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/json-generative-ui.mdx +0 -42
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/rendering.mdx +0 -84
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/slack.mdx +0 -85
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/spec.mdx +0 -45
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/teams.mdx +0 -93
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/tokens.mdx +0 -62
- package/.docs/raw/docs/(reference)/api-reference/hooks/composer-triggers.mdx +0 -129
- package/.docs/raw/docs/(reference)/api-reference/hooks/index.mdx +0 -31
- package/.docs/raw/docs/(reference)/api-reference/hooks/model-context.mdx +0 -34
- package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +0 -350
- package/.docs/raw/docs/(reference)/api-reference/hooks/runtimes.mdx +0 -28
- package/.docs/raw/docs/(reference)/api-reference/hooks/state.mdx +0 -97
- package/.docs/raw/docs/(reference)/api-reference/integrations/ai-sdk.mdx +0 -200
- package/.docs/raw/docs/(reference)/api-reference/integrations/cloud-ai-sdk.mdx +0 -24
- package/.docs/raw/docs/(reference)/api-reference/integrations/eve.mdx +0 -118
- package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +0 -28
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +0 -37
- package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +0 -50
- package/.docs/raw/docs/(reference)/api-reference/model-context/index.mdx +0 -22
- package/.docs/raw/docs/(reference)/api-reference/model-context/registry.mdx +0 -20
- package/.docs/raw/docs/(reference)/api-reference/overview.mdx +0 -566
- package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar-more.mdx +0 -152
- package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +0 -239
- package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +0 -208
- package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-modal.mdx +0 -108
- package/.docs/raw/docs/(reference)/api-reference/primitives/attachment.mdx +0 -99
- package/.docs/raw/docs/(reference)/api-reference/primitives/branch-picker.mdx +0 -145
- package/.docs/raw/docs/(reference)/api-reference/primitives/chain-of-thought.mdx +0 -82
- package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +0 -637
- package/.docs/raw/docs/(reference)/api-reference/primitives/composition.mdx +0 -22
- package/.docs/raw/docs/(reference)/api-reference/primitives/error.mdx +0 -65
- package/.docs/raw/docs/(reference)/api-reference/primitives/index.mdx +0 -73
- package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +0 -117
- package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +0 -339
- package/.docs/raw/docs/(reference)/api-reference/primitives/queue-item.mdx +0 -77
- package/.docs/raw/docs/(reference)/api-reference/primitives/selection-toolbar.mdx +0 -88
- package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +0 -81
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item-more.mdx +0 -129
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item.mdx +0 -138
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list.mdx +0 -122
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +0 -289
- package/.docs/raw/docs/(reference)/api-reference/runtimes/assistant-runtime.mdx +0 -20
- package/.docs/raw/docs/(reference)/api-reference/runtimes/attachment-runtime.mdx +0 -24
- package/.docs/raw/docs/(reference)/api-reference/runtimes/composer-runtime.mdx +0 -32
- package/.docs/raw/docs/(reference)/api-reference/runtimes/index.mdx +0 -43
- package/.docs/raw/docs/(reference)/api-reference/runtimes/message-part-runtime.mdx +0 -43
- package/.docs/raw/docs/(reference)/api-reference/runtimes/message-runtime.mdx +0 -24
- package/.docs/raw/docs/(reference)/api-reference/runtimes/queue-state.mdx +0 -20
- package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-item-runtime.mdx +0 -24
- package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +0 -24
- package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-runtime.mdx +0 -36
- package/.docs/raw/docs/(reference)/api-reference/tools/component-tools.mdx +0 -85
- package/.docs/raw/docs/(reference)/api-reference/tools/index.mdx +0 -45
- package/.docs/raw/docs/(reference)/api-reference/tools/interactables-legacy.mdx +0 -55
- package/.docs/raw/docs/(reference)/api-reference/tools/interactables.mdx +0 -151
- package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +0 -106
- package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +0 -62
- package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +0 -178
- package/.docs/raw/docs/(reference)/api-reference/transport/assistant-transport.mdx +0 -48
- package/.docs/raw/docs/(reference)/api-reference/transport/frame.mdx +0 -62
- package/.docs/raw/docs/(reference)/api-reference/transport/index.mdx +0 -22
- package/.docs/raw/docs/(reference)/api-reference/utilities/index.mdx +0 -19
- package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +0 -152
- package/.docs/raw/docs/(reference)/api-reference/voice/index.mdx +0 -22
- package/.docs/raw/docs/(reference)/api-reference/voice/session.mdx +0 -54
- package/.docs/raw/docs/(reference)/api-reference/voice/speech-dictation.mdx +0 -36
- package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +0 -527
- package/.docs/raw/docs/cloud/ai-sdk.mdx +0 -604
- package/.docs/raw/docs/cloud/authorization.mdx +0 -252
- package/.docs/raw/docs/cloud/index.mdx +0 -46
- package/.docs/raw/docs/cloud/langgraph.mdx +0 -554
- package/.docs/raw/docs/copilots/assistant-frame.mdx +0 -411
- package/.docs/raw/docs/copilots/make-assistant-visible.mdx +0 -82
- package/.docs/raw/docs/copilots/model-context.mdx +0 -156
- package/.docs/raw/docs/copilots/motivation.mdx +0 -202
- package/.docs/raw/docs/copilots/use-assistant-instructions.mdx +0 -64
- package/.docs/raw/docs/guides/attachments.mdx +0 -619
- package/.docs/raw/docs/guides/branching.mdx +0 -76
- package/.docs/raw/docs/guides/chain-of-thought.mdx +0 -164
- package/.docs/raw/docs/guides/chatgpt-subscription.mdx +0 -108
- package/.docs/raw/docs/guides/context-api.mdx +0 -619
- package/.docs/raw/docs/guides/dictation.mdx +0 -298
- package/.docs/raw/docs/guides/editing.mdx +0 -102
- package/.docs/raw/docs/guides/electron.mdx +0 -369
- package/.docs/raw/docs/guides/headless-composer-input.mdx +0 -113
- package/.docs/raw/docs/guides/image-generation.mdx +0 -74
- package/.docs/raw/docs/guides/index.mdx +0 -117
- package/.docs/raw/docs/guides/input-history.mdx +0 -55
- package/.docs/raw/docs/guides/latex.mdx +0 -158
- package/.docs/raw/docs/guides/mentions.mdx +0 -575
- package/.docs/raw/docs/guides/message-timing.mdx +0 -215
- package/.docs/raw/docs/guides/quoting.mdx +0 -169
- package/.docs/raw/docs/guides/resumable-stream-deployment.mdx +0 -212
- package/.docs/raw/docs/guides/resumable-stream-stores.mdx +0 -152
- package/.docs/raw/docs/guides/resumable-streams.mdx +0 -292
- package/.docs/raw/docs/guides/slash-commands.mdx +0 -358
- package/.docs/raw/docs/guides/speech.mdx +0 -172
- package/.docs/raw/docs/guides/suggestions.mdx +0 -384
- package/.docs/raw/docs/guides/virtualization.mdx +0 -133
- package/.docs/raw/docs/guides/voice.mdx +0 -310
- package/.docs/raw/docs/ink/adapters.mdx +0 -99
- package/.docs/raw/docs/ink/custom-backend.mdx +0 -254
- package/.docs/raw/docs/ink/hooks.mdx +0 -469
- package/.docs/raw/docs/ink/index.mdx +0 -237
- package/.docs/raw/docs/ink/migration.mdx +0 -138
- package/.docs/raw/docs/ink/primitives.mdx +0 -1259
- package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +0 -528
- package/.docs/raw/docs/integrations/auth/better-auth.mdx +0 -188
- package/.docs/raw/docs/integrations/auth/clerk.mdx +0 -166
- package/.docs/raw/docs/integrations/auth/next-auth.mdx +0 -191
- package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +0 -88
- package/.docs/raw/docs/integrations/frameworks/cloudflare-agents.mdx +0 -283
- package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +0 -188
- package/.docs/raw/docs/integrations/frameworks/mastra/overview.mdx +0 -57
- package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +0 -201
- package/.docs/raw/docs/integrations/gateways/index.mdx +0 -162
- package/.docs/raw/docs/integrations/index.mdx +0 -178
- package/.docs/raw/docs/integrations/observability/helicone.mdx +0 -129
- package/.docs/raw/docs/integrations/observability/langfuse.mdx +0 -161
- package/.docs/raw/docs/integrations/observability/langsmith.mdx +0 -151
- package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +0 -773
- package/.docs/raw/docs/migrations/deprecation-policy.mdx +0 -42
- package/.docs/raw/docs/migrations/index.mdx +0 -50
- package/.docs/raw/docs/migrations/react-compatibility.mdx +0 -74
- package/.docs/raw/docs/migrations/react-langgraph-v0-7.mdx +0 -328
- package/.docs/raw/docs/migrations/toolkit-tools.mdx +0 -236
- package/.docs/raw/docs/migrations/v0-11.mdx +0 -172
- package/.docs/raw/docs/migrations/v0-12.mdx +0 -302
- package/.docs/raw/docs/migrations/v0-14.mdx +0 -297
- package/.docs/raw/docs/migrations/v0-15.mdx +0 -270
- package/.docs/raw/docs/primitives/action-bar.mdx +0 -352
- package/.docs/raw/docs/primitives/assistant-modal.mdx +0 -216
- package/.docs/raw/docs/primitives/attachment.mdx +0 -217
- package/.docs/raw/docs/primitives/branch-picker.mdx +0 -222
- package/.docs/raw/docs/primitives/chain-of-thought.mdx +0 -315
- package/.docs/raw/docs/primitives/composer.mdx +0 -659
- package/.docs/raw/docs/primitives/error.mdx +0 -142
- package/.docs/raw/docs/primitives/index.mdx +0 -99
- package/.docs/raw/docs/primitives/message.mdx +0 -618
- package/.docs/raw/docs/primitives/selection-toolbar.mdx +0 -191
- package/.docs/raw/docs/primitives/suggestion.mdx +0 -254
- package/.docs/raw/docs/primitives/thread-list.mdx +0 -467
- package/.docs/raw/docs/primitives/thread.mdx +0 -509
- package/.docs/raw/docs/react-native/adapters.mdx +0 -94
- package/.docs/raw/docs/react-native/custom-backend.mdx +0 -207
- package/.docs/raw/docs/react-native/hooks.mdx +0 -339
- package/.docs/raw/docs/react-native/index.mdx +0 -289
- package/.docs/raw/docs/react-native/migration.mdx +0 -142
- package/.docs/raw/docs/react-native/primitives.mdx +0 -976
- package/.docs/raw/docs/runtimes/a2a/client-and-hooks.mdx +0 -399
- package/.docs/raw/docs/runtimes/a2a/overview.mdx +0 -60
- package/.docs/raw/docs/runtimes/a2a/quickstart.mdx +0 -216
- package/.docs/raw/docs/runtimes/ag-ui/agent-state.mdx +0 -124
- package/.docs/raw/docs/runtimes/ag-ui/overview.mdx +0 -70
- package/.docs/raw/docs/runtimes/ag-ui/quickstart.mdx +0 -243
- package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +0 -267
- package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +0 -61
- package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +0 -122
- package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +0 -135
- package/.docs/raw/docs/runtimes/ai-sdk/v6-legacy.mdx +0 -636
- package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +0 -731
- package/.docs/raw/docs/runtimes/claude-managed-agents.mdx +0 -118
- package/.docs/raw/docs/runtimes/concepts/adapters.mdx +0 -265
- package/.docs/raw/docs/runtimes/concepts/architecture.mdx +0 -167
- package/.docs/raw/docs/runtimes/concepts/stability.mdx +0 -68
- package/.docs/raw/docs/runtimes/concepts/threads.mdx +0 -513
- package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +0 -763
- package/.docs/raw/docs/runtimes/custom/data-stream.mdx +0 -339
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +0 -957
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +0 -902
- package/.docs/raw/docs/runtimes/custom/overview.mdx +0 -71
- package/.docs/raw/docs/runtimes/eve/overview.mdx +0 -151
- package/.docs/raw/docs/runtimes/eve/quickstart.mdx +0 -192
- package/.docs/raw/docs/runtimes/google-adk/api.mdx +0 -256
- package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +0 -784
- package/.docs/raw/docs/runtimes/google-adk/overview.mdx +0 -69
- package/.docs/raw/docs/runtimes/google-adk/quickstart.mdx +0 -229
- package/.docs/raw/docs/runtimes/langchain.mdx +0 -901
- package/.docs/raw/docs/runtimes/langgraph/agent-state.mdx +0 -181
- package/.docs/raw/docs/runtimes/langgraph/generative-ui.mdx +0 -305
- package/.docs/raw/docs/runtimes/langgraph/interrupts.mdx +0 -104
- package/.docs/raw/docs/runtimes/langgraph/overview.mdx +0 -84
- package/.docs/raw/docs/runtimes/langgraph/quickstart.mdx +0 -496
- package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +0 -144
- package/.docs/raw/docs/runtimes/langgraph/threads.mdx +0 -113
- package/.docs/raw/docs/runtimes/langgraph/tutorial/introduction.mdx +0 -29
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-1.mdx +0 -93
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +0 -347
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +0 -393
- package/.docs/raw/docs/runtimes/opencode/hooks.mdx +0 -194
- package/.docs/raw/docs/runtimes/opencode/overview.mdx +0 -58
- package/.docs/raw/docs/runtimes/opencode/quickstart.mdx +0 -119
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +0 -133
- package/.docs/raw/docs/tools/a2ui.mdx +0 -107
- package/.docs/raw/docs/tools/backend.mdx +0 -147
- package/.docs/raw/docs/tools/defining-tools.mdx +0 -574
- package/.docs/raw/docs/tools/dynamic-tools.mdx +0 -112
- package/.docs/raw/docs/tools/generative-ui-primitive.mdx +0 -180
- package/.docs/raw/docs/tools/generative-ui-slack.mdx +0 -167
- package/.docs/raw/docs/tools/generative-ui-teams.mdx +0 -160
- package/.docs/raw/docs/tools/generative-ui.mdx +0 -322
- package/.docs/raw/docs/tools/index.mdx +0 -72
- package/.docs/raw/docs/tools/interactables.mdx +0 -1084
- package/.docs/raw/docs/tools/mcp-apps.mdx +0 -363
- package/.docs/raw/docs/tools/mcp.mdx +0 -466
- package/.docs/raw/docs/tools/multi-agent.mdx +0 -237
- package/.docs/raw/docs/tools/openui.mdx +0 -175
- package/.docs/raw/docs/tools/tool-ui.mdx +0 -1020
- package/.docs/raw/docs/tools/user-managed-mcp.mdx +0 -428
- package/.docs/raw/docs/ui/assistant-modal.mdx +0 -166
- package/.docs/raw/docs/ui/assistant-sidebar.mdx +0 -89
- package/.docs/raw/docs/ui/attachment.mdx +0 -279
- package/.docs/raw/docs/ui/composer-trigger-popover.mdx +0 -247
- package/.docs/raw/docs/ui/context-display.mdx +0 -148
- package/.docs/raw/docs/ui/directive-text.mdx +0 -114
- package/.docs/raw/docs/ui/file.mdx +0 -159
- package/.docs/raw/docs/ui/follow-up-suggestions.mdx +0 -82
- package/.docs/raw/docs/ui/image.mdx +0 -102
- package/.docs/raw/docs/ui/markdown.mdx +0 -103
- package/.docs/raw/docs/ui/mcp-config.mdx +0 -107
- package/.docs/raw/docs/ui/mermaid.mdx +0 -92
- package/.docs/raw/docs/ui/message-timing.mdx +0 -93
- package/.docs/raw/docs/ui/model-selector.mdx +0 -455
- package/.docs/raw/docs/ui/part-grouping.mdx +0 -614
- package/.docs/raw/docs/ui/quote.mdx +0 -211
- package/.docs/raw/docs/ui/reasoning.mdx +0 -214
- package/.docs/raw/docs/ui/scrollbar.mdx +0 -77
- package/.docs/raw/docs/ui/sources.mdx +0 -186
- package/.docs/raw/docs/ui/streamdown.mdx +0 -392
- package/.docs/raw/docs/ui/syntax-highlighting.mdx +0 -205
- package/.docs/raw/docs/ui/thread-list.mdx +0 -411
- package/.docs/raw/docs/ui/thread.mdx +0 -559
- package/.docs/raw/docs/ui/tool-fallback.mdx +0 -141
- package/.docs/raw/docs/ui/tool-group.mdx +0 -240
- package/.docs/raw/docs/ui/voice.mdx +0 -173
- package/.docs/raw/docs/utilities/heat-graph.mdx +0 -237
- package/.docs/raw/docs/utilities/react-o11y.mdx +0 -362
- package/.docs/raw/docs/utilities/tw-shimmer.mdx +0 -212
- package/dist/constants.d.ts +0 -14
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js +0 -19
- package/dist/constants.js.map +0 -1
- package/dist/prepare-docs/code-examples.d.ts +0 -5
- package/dist/prepare-docs/code-examples.d.ts.map +0 -1
- package/dist/prepare-docs/code-examples.js +0 -117
- package/dist/prepare-docs/code-examples.js.map +0 -1
- package/dist/prepare-docs/copy-raw.d.ts +0 -5
- package/dist/prepare-docs/copy-raw.d.ts.map +0 -1
- package/dist/prepare-docs/copy-raw.js +0 -49
- package/dist/prepare-docs/copy-raw.js.map +0 -1
- package/dist/prepare-docs/prepare.d.ts +0 -1
- package/dist/prepare-docs/prepare.js +0 -22
- package/dist/prepare-docs/prepare.js.map +0 -1
- package/dist/prompts/xulux-playground.d.ts +0 -12
- package/dist/prompts/xulux-playground.d.ts.map +0 -1
- package/dist/prompts/xulux-playground.js +0 -33
- package/dist/prompts/xulux-playground.js.map +0 -1
- package/dist/tools/docs.d.ts +0 -22
- package/dist/tools/docs.d.ts.map +0 -1
- package/dist/tools/docs.js +0 -177
- package/dist/tools/docs.js.map +0 -1
- package/dist/tools/examples.d.ts +0 -24
- package/dist/tools/examples.d.ts.map +0 -1
- package/dist/tools/examples.js +0 -87
- package/dist/tools/examples.js.map +0 -1
- package/dist/tools/resources.d.ts +0 -6
- package/dist/tools/resources.d.ts.map +0 -1
- package/dist/tools/resources.js +0 -75
- package/dist/tools/resources.js.map +0 -1
- package/dist/tools/search.d.ts +0 -24
- package/dist/tools/search.d.ts.map +0 -1
- package/dist/tools/search.js +0 -39
- package/dist/tools/search.js.map +0 -1
- package/dist/tools/tests/mcp-test-client.d.ts +0 -15
- package/dist/tools/tests/mcp-test-client.d.ts.map +0 -1
- package/dist/tools/tests/mcp-test-client.js +0 -68
- package/dist/tools/tests/mcp-test-client.js.map +0 -1
- package/dist/tools/tests/test-setup.d.ts +0 -7
- package/dist/tools/tests/test-setup.d.ts.map +0 -1
- package/dist/tools/tests/test-setup.js +0 -36
- package/dist/tools/tests/test-setup.js.map +0 -1
- package/dist/tools/xulux-templates.d.ts +0 -58
- package/dist/tools/xulux-templates.d.ts.map +0 -1
- package/dist/tools/xulux-templates.js +0 -84
- package/dist/tools/xulux-templates.js.map +0 -1
- package/dist/utils/cache.d.ts +0 -5
- package/dist/utils/cache.d.ts.map +0 -1
- package/dist/utils/cache.js +0 -18
- package/dist/utils/cache.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -10
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -20
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/mcp-format.d.ts +0 -11
- package/dist/utils/mcp-format.d.ts.map +0 -1
- package/dist/utils/mcp-format.js +0 -14
- package/dist/utils/mcp-format.js.map +0 -1
- package/dist/utils/mdx.d.ts +0 -12
- package/dist/utils/mdx.d.ts.map +0 -1
- package/dist/utils/mdx.js +0 -45
- package/dist/utils/mdx.js.map +0 -1
- package/dist/utils/paths.d.ts +0 -12
- package/dist/utils/paths.d.ts.map +0 -1
- package/dist/utils/paths.js +0 -93
- package/dist/utils/paths.js.map +0 -1
- package/dist/utils/search.d.ts +0 -10
- package/dist/utils/search.d.ts.map +0 -1
- package/dist/utils/search.js +0 -97
- package/dist/utils/search.js.map +0 -1
- package/dist/utils/security.d.ts +0 -5
- package/dist/utils/security.d.ts.map +0 -1
- package/dist/utils/security.js +0 -22
- package/dist/utils/security.js.map +0 -1
- package/dist/xulux/catalog-client.d.ts +0 -14
- package/dist/xulux/catalog-client.d.ts.map +0 -1
- package/dist/xulux/catalog-client.js +0 -112
- package/dist/xulux/catalog-client.js.map +0 -1
- package/dist/xulux/fallback-catalog.d.ts +0 -7
- package/dist/xulux/fallback-catalog.d.ts.map +0 -1
- package/dist/xulux/fallback-catalog.js +0 -47
- package/dist/xulux/fallback-catalog.js.map +0 -1
- package/dist/xulux/fetch-sandbox.d.ts +0 -5
- package/dist/xulux/fetch-sandbox.d.ts.map +0 -1
- package/dist/xulux/fetch-sandbox.js +0 -40
- package/dist/xulux/fetch-sandbox.js.map +0 -1
- package/dist/xulux/template-service.d.ts +0 -84
- package/dist/xulux/template-service.d.ts.map +0 -1
- package/dist/xulux/template-service.js +0 -223
- package/dist/xulux/template-service.js.map +0 -1
- package/dist/xulux/types.d.ts +0 -55
- package/dist/xulux/types.d.ts.map +0 -1
- package/dist/xulux/types.js +0 -6
- package/dist/xulux/types.js.map +0 -1
- package/src/constants.ts +0 -24
- package/src/prepare-docs/code-examples.ts +0 -158
- package/src/prepare-docs/copy-raw.ts +0 -50
- package/src/prepare-docs/prepare.ts +0 -24
- package/src/prompts/xulux-playground.ts +0 -36
- package/src/tools/docs.ts +0 -255
- package/src/tools/examples.ts +0 -114
- package/src/tools/resources.ts +0 -111
- package/src/tools/search.ts +0 -46
- package/src/tools/tests/completions.test.ts +0 -60
- package/src/tools/tests/directory-size-cap.test.ts +0 -50
- package/src/tools/tests/docs.test.ts +0 -147
- package/src/tools/tests/examples.test.ts +0 -94
- package/src/tools/tests/integration.test.ts +0 -45
- package/src/tools/tests/json-parsing.test.ts +0 -23
- package/src/tools/tests/listings-cache.test.ts +0 -19
- package/src/tools/tests/mcp-protocol.test.ts +0 -216
- package/src/tools/tests/mcp-test-client.ts +0 -111
- package/src/tools/tests/path-traversal.test.ts +0 -84
- package/src/tools/tests/resources.test.ts +0 -133
- package/src/tools/tests/search.test.ts +0 -37
- package/src/tools/tests/test-setup.ts +0 -50
- package/src/tools/tests/xulux-templates.test.ts +0 -325
- package/src/tools/xulux-templates.ts +0 -141
- package/src/utils/cache.ts +0 -20
- package/src/utils/logger.ts +0 -20
- package/src/utils/mcp-format.ts +0 -14
- package/src/utils/mdx.ts +0 -59
- package/src/utils/paths.ts +0 -139
- package/src/utils/search.ts +0 -131
- package/src/utils/security.ts +0 -52
- package/src/utils/tests/cache.test.ts +0 -51
- package/src/utils/tests/mcp-format.test.ts +0 -22
- package/src/utils/tests/security.test.ts +0 -119
- package/src/xulux/catalog-client.ts +0 -150
- package/src/xulux/fallback-catalog.ts +0 -63
- package/src/xulux/fetch-sandbox.ts +0 -56
- package/src/xulux/template-service.ts +0 -406
- package/src/xulux/types.ts +0 -64
|
@@ -1,2084 +0,0 @@
|
|
|
1
|
-
# Example: with-pi
|
|
2
|
-
|
|
3
|
-
## app/api/pi/fs/route.ts
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
import { readdir } from "node:fs/promises";
|
|
7
|
-
import { homedir } from "node:os";
|
|
8
|
-
import { resolve, dirname, join } from "node:path";
|
|
9
|
-
import { withFail } from "@/lib/http";
|
|
10
|
-
|
|
11
|
-
export const runtime = "nodejs";
|
|
12
|
-
export const dynamic = "force-dynamic";
|
|
13
|
-
|
|
14
|
-
export type FsEntry = { name: string; path: string };
|
|
15
|
-
export type FsListing = {
|
|
16
|
-
path: string;
|
|
17
|
-
parent: string | null;
|
|
18
|
-
entries: FsEntry[];
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const expand = (p: string): string =>
|
|
22
|
-
p.startsWith("~") ? join(homedir(), p.slice(1)) : p;
|
|
23
|
-
|
|
24
|
-
export const GET = withFail(async (request: Request) => {
|
|
25
|
-
// This lists arbitrary host directories for the local workspace picker —
|
|
26
|
-
// never expose it from a deployed instance.
|
|
27
|
-
if (process.env.NODE_ENV === "production") {
|
|
28
|
-
return new Response(null, { status: 404 });
|
|
29
|
-
}
|
|
30
|
-
const raw = new URL(request.url).searchParams.get("path");
|
|
31
|
-
const dir = resolve(raw ? expand(raw) : homedir());
|
|
32
|
-
const dirents = await readdir(dir, { withFileTypes: true });
|
|
33
|
-
const entries = dirents
|
|
34
|
-
.filter((dirent) => dirent.isDirectory() && !dirent.name.startsWith("."))
|
|
35
|
-
.map((dirent) => ({ name: dirent.name, path: join(dir, dirent.name) }))
|
|
36
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
37
|
-
const parent = dirname(dir);
|
|
38
|
-
return Response.json({
|
|
39
|
-
path: dir,
|
|
40
|
-
parent: parent === dir ? null : parent,
|
|
41
|
-
entries,
|
|
42
|
-
} satisfies FsListing);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## app/api/pi/handshake/route.ts
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
import { getHandshake } from "@/lib/pi-server";
|
|
51
|
-
import { withFail } from "@/lib/http";
|
|
52
|
-
|
|
53
|
-
export const runtime = "nodejs";
|
|
54
|
-
export const dynamic = "force-dynamic";
|
|
55
|
-
|
|
56
|
-
export const GET = withFail(async () => Response.json(await getHandshake()));
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## app/api/pi/models/route.ts
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
import { piClient } from "@/lib/pi-server";
|
|
64
|
-
import { withFail } from "@/lib/http";
|
|
65
|
-
|
|
66
|
-
export const runtime = "nodejs";
|
|
67
|
-
export const dynamic = "force-dynamic";
|
|
68
|
-
|
|
69
|
-
export const GET = withFail(async () =>
|
|
70
|
-
Response.json(await piClient.getAvailableModels()),
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## app/api/pi/threads/[threadId]/archive/route.ts
|
|
76
|
-
|
|
77
|
-
```typescript
|
|
78
|
-
import type { NextRequest } from "next/server";
|
|
79
|
-
import { piClient } from "@/lib/pi-server";
|
|
80
|
-
import { noContent, withFail } from "@/lib/http";
|
|
81
|
-
|
|
82
|
-
export const runtime = "nodejs";
|
|
83
|
-
export const dynamic = "force-dynamic";
|
|
84
|
-
|
|
85
|
-
type Context = { params: Promise<{ threadId: string }> };
|
|
86
|
-
|
|
87
|
-
export const POST = withFail(async (_req: NextRequest, { params }: Context) => {
|
|
88
|
-
const { threadId } = await params;
|
|
89
|
-
await piClient.archiveThread(threadId);
|
|
90
|
-
return noContent();
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## app/api/pi/threads/[threadId]/cancel/route.ts
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
import type { NextRequest } from "next/server";
|
|
99
|
-
import { piClient } from "@/lib/pi-server";
|
|
100
|
-
import { noContent, withFail } from "@/lib/http";
|
|
101
|
-
|
|
102
|
-
export const runtime = "nodejs";
|
|
103
|
-
export const dynamic = "force-dynamic";
|
|
104
|
-
|
|
105
|
-
type Context = { params: Promise<{ threadId: string }> };
|
|
106
|
-
|
|
107
|
-
export const POST = withFail(async (_req: NextRequest, { params }: Context) => {
|
|
108
|
-
const { threadId } = await params;
|
|
109
|
-
await piClient.cancelRun(threadId);
|
|
110
|
-
return noContent();
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## app/api/pi/threads/[threadId]/events/route.ts
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
import type { NextRequest } from "next/server";
|
|
119
|
-
import { piClient } from "@/lib/pi-server";
|
|
120
|
-
|
|
121
|
-
export const runtime = "nodejs";
|
|
122
|
-
export const dynamic = "force-dynamic";
|
|
123
|
-
|
|
124
|
-
type Context = { params: Promise<{ threadId: string }> };
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* SSE stream of `PiClientEvent`s for one thread. Snapshot-first by default;
|
|
128
|
-
* `?snapshot=false` lets a client that already called `GET /threads/:id` attach
|
|
129
|
-
* only to subsequent live events. A client disconnect unsubscribes but does NOT
|
|
130
|
-
* abort the Pi run — disconnect ≠ abort.
|
|
131
|
-
*/
|
|
132
|
-
export async function GET(req: NextRequest, { params }: Context) {
|
|
133
|
-
const { threadId } = await params;
|
|
134
|
-
const encoder = new TextEncoder();
|
|
135
|
-
let unsubscribe: (() => void) | undefined;
|
|
136
|
-
let heartbeat: ReturnType<typeof setInterval> | undefined;
|
|
137
|
-
const stream = new TransformStream<Uint8Array, Uint8Array>();
|
|
138
|
-
const writer = stream.writable.getWriter();
|
|
139
|
-
|
|
140
|
-
const write = (chunk: string) => {
|
|
141
|
-
void writer.write(encoder.encode(chunk)).catch(() => {
|
|
142
|
-
// Writer already closed (client gone); drop.
|
|
143
|
-
});
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const cleanup = () => {
|
|
147
|
-
unsubscribe?.();
|
|
148
|
-
if (heartbeat) clearInterval(heartbeat);
|
|
149
|
-
void writer.close().catch(() => {
|
|
150
|
-
// Already closed.
|
|
151
|
-
});
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
// Flush headers through any buffering proxy immediately.
|
|
155
|
-
write(": connected\n\n");
|
|
156
|
-
// Keep-alive under typical proxy idle timeouts (30–60s) without the write
|
|
157
|
-
// churn of a tighter interval.
|
|
158
|
-
heartbeat = setInterval(() => write(": ping\n\n"), 20_000);
|
|
159
|
-
unsubscribe = piClient.subscribe(
|
|
160
|
-
threadId,
|
|
161
|
-
(event) => {
|
|
162
|
-
write(`data: ${JSON.stringify(event)}\n\n`);
|
|
163
|
-
},
|
|
164
|
-
{ includeSnapshot: req.nextUrl.searchParams.get("snapshot") !== "false" },
|
|
165
|
-
);
|
|
166
|
-
req.signal.addEventListener("abort", cleanup, { once: true });
|
|
167
|
-
|
|
168
|
-
return new Response(stream.readable, {
|
|
169
|
-
headers: {
|
|
170
|
-
"content-type": "text/event-stream; charset=utf-8",
|
|
171
|
-
"cache-control": "no-cache, no-transform",
|
|
172
|
-
connection: "keep-alive",
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## app/api/pi/threads/[threadId]/host-ui/route.ts
|
|
180
|
-
|
|
181
|
-
```typescript
|
|
182
|
-
import type { NextRequest } from "next/server";
|
|
183
|
-
import type { PiHostUiResponse } from "@assistant-ui/react-pi";
|
|
184
|
-
import { piClient } from "@/lib/pi-server";
|
|
185
|
-
import { noContent, withFail } from "@/lib/http";
|
|
186
|
-
|
|
187
|
-
export const runtime = "nodejs";
|
|
188
|
-
export const dynamic = "force-dynamic";
|
|
189
|
-
|
|
190
|
-
type Context = { params: Promise<{ threadId: string }> };
|
|
191
|
-
|
|
192
|
-
export const POST = withFail(async (req: NextRequest, { params }: Context) => {
|
|
193
|
-
const { threadId } = await params;
|
|
194
|
-
const { response } = (await req.json()) as { response: PiHostUiResponse };
|
|
195
|
-
await piClient.respondToHostUiRequest(threadId, response);
|
|
196
|
-
return noContent();
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
## app/api/pi/threads/[threadId]/messages/route.ts
|
|
202
|
-
|
|
203
|
-
```typescript
|
|
204
|
-
import type { NextRequest } from "next/server";
|
|
205
|
-
import type { PiSendMessageInput } from "@assistant-ui/react-pi";
|
|
206
|
-
import { piClient } from "@/lib/pi-server";
|
|
207
|
-
import { noContent, withFail } from "@/lib/http";
|
|
208
|
-
|
|
209
|
-
export const runtime = "nodejs";
|
|
210
|
-
export const dynamic = "force-dynamic";
|
|
211
|
-
|
|
212
|
-
type Context = { params: Promise<{ threadId: string }> };
|
|
213
|
-
|
|
214
|
-
export const POST = withFail(async (req: NextRequest, { params }: Context) => {
|
|
215
|
-
const { threadId } = await params;
|
|
216
|
-
const { input } = (await req.json()) as { input: PiSendMessageInput };
|
|
217
|
-
await piClient.sendMessage(threadId, input);
|
|
218
|
-
return noContent();
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
## app/api/pi/threads/[threadId]/model/route.ts
|
|
224
|
-
|
|
225
|
-
```typescript
|
|
226
|
-
import type { NextRequest } from "next/server";
|
|
227
|
-
import { piClient } from "@/lib/pi-server";
|
|
228
|
-
import { noContent, withFail } from "@/lib/http";
|
|
229
|
-
|
|
230
|
-
export const runtime = "nodejs";
|
|
231
|
-
export const dynamic = "force-dynamic";
|
|
232
|
-
|
|
233
|
-
type Context = { params: Promise<{ threadId: string }> };
|
|
234
|
-
|
|
235
|
-
export const POST = withFail(async (req: NextRequest, { params }: Context) => {
|
|
236
|
-
const { threadId } = await params;
|
|
237
|
-
const input = (await req.json()) as { provider: string; modelId: string };
|
|
238
|
-
await piClient.setModel(threadId, input);
|
|
239
|
-
return noContent();
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
## app/api/pi/threads/[threadId]/queue/clear/route.ts
|
|
245
|
-
|
|
246
|
-
```typescript
|
|
247
|
-
import type { NextRequest } from "next/server";
|
|
248
|
-
import { piClient } from "@/lib/pi-server";
|
|
249
|
-
import { withFail } from "@/lib/http";
|
|
250
|
-
|
|
251
|
-
export const runtime = "nodejs";
|
|
252
|
-
export const dynamic = "force-dynamic";
|
|
253
|
-
|
|
254
|
-
type Context = { params: Promise<{ threadId: string }> };
|
|
255
|
-
|
|
256
|
-
export const POST = withFail(async (_req: NextRequest, { params }: Context) => {
|
|
257
|
-
const { threadId } = await params;
|
|
258
|
-
return Response.json(await piClient.clearQueue(threadId));
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
## app/api/pi/threads/[threadId]/route.ts
|
|
264
|
-
|
|
265
|
-
```typescript
|
|
266
|
-
import type { NextRequest } from "next/server";
|
|
267
|
-
import { piClient } from "@/lib/pi-server";
|
|
268
|
-
import { badRequest, noContent, withFail } from "@/lib/http";
|
|
269
|
-
|
|
270
|
-
export const runtime = "nodejs";
|
|
271
|
-
export const dynamic = "force-dynamic";
|
|
272
|
-
|
|
273
|
-
type Context = { params: Promise<{ threadId: string }> };
|
|
274
|
-
|
|
275
|
-
export const GET = withFail(async (_req: NextRequest, { params }: Context) => {
|
|
276
|
-
const { threadId } = await params;
|
|
277
|
-
return Response.json(await piClient.getThread(threadId));
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
export const PATCH = withFail(async (req: NextRequest, { params }: Context) => {
|
|
281
|
-
const { threadId } = await params;
|
|
282
|
-
const { title } = (await req.json()) as { title?: unknown };
|
|
283
|
-
if (typeof title !== "string" || title.trim() === "") {
|
|
284
|
-
return badRequest("title must be a non-empty string");
|
|
285
|
-
}
|
|
286
|
-
await piClient.renameThread(threadId, title);
|
|
287
|
-
return noContent();
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
export const DELETE = withFail(
|
|
291
|
-
async (_req: NextRequest, { params }: Context) => {
|
|
292
|
-
const { threadId } = await params;
|
|
293
|
-
await piClient.deleteThread(threadId);
|
|
294
|
-
return noContent();
|
|
295
|
-
},
|
|
296
|
-
);
|
|
297
|
-
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
## app/api/pi/threads/[threadId]/thinking/route.ts
|
|
301
|
-
|
|
302
|
-
```typescript
|
|
303
|
-
import type { NextRequest } from "next/server";
|
|
304
|
-
import type { PiThinkingLevel } from "@assistant-ui/react-pi";
|
|
305
|
-
import { piClient } from "@/lib/pi-server";
|
|
306
|
-
import { noContent, withFail } from "@/lib/http";
|
|
307
|
-
|
|
308
|
-
export const runtime = "nodejs";
|
|
309
|
-
export const dynamic = "force-dynamic";
|
|
310
|
-
|
|
311
|
-
type Context = { params: Promise<{ threadId: string }> };
|
|
312
|
-
|
|
313
|
-
export const POST = withFail(async (req: NextRequest, { params }: Context) => {
|
|
314
|
-
const { threadId } = await params;
|
|
315
|
-
const { level } = (await req.json()) as { level: PiThinkingLevel };
|
|
316
|
-
await piClient.setThinkingLevel(threadId, level);
|
|
317
|
-
return noContent();
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
## app/api/pi/threads/[threadId]/unarchive/route.ts
|
|
323
|
-
|
|
324
|
-
```typescript
|
|
325
|
-
import type { NextRequest } from "next/server";
|
|
326
|
-
import { piClient } from "@/lib/pi-server";
|
|
327
|
-
import { noContent, withFail } from "@/lib/http";
|
|
328
|
-
|
|
329
|
-
export const runtime = "nodejs";
|
|
330
|
-
export const dynamic = "force-dynamic";
|
|
331
|
-
|
|
332
|
-
type Context = { params: Promise<{ threadId: string }> };
|
|
333
|
-
|
|
334
|
-
export const POST = withFail(async (_req: NextRequest, { params }: Context) => {
|
|
335
|
-
const { threadId } = await params;
|
|
336
|
-
await piClient.unarchiveThread(threadId);
|
|
337
|
-
return noContent();
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
## app/api/pi/threads/route.ts
|
|
343
|
-
|
|
344
|
-
```typescript
|
|
345
|
-
import type { NextRequest } from "next/server";
|
|
346
|
-
import { piClient } from "@/lib/pi-server";
|
|
347
|
-
import { withFail } from "@/lib/http";
|
|
348
|
-
|
|
349
|
-
export const runtime = "nodejs";
|
|
350
|
-
export const dynamic = "force-dynamic";
|
|
351
|
-
|
|
352
|
-
export const GET = withFail(async (req: NextRequest) => {
|
|
353
|
-
const params = new URL(req.url).searchParams;
|
|
354
|
-
const workspacePath = params.get("workspacePath") ?? undefined;
|
|
355
|
-
const includeArchived = params.get("includeArchived") === "true";
|
|
356
|
-
const threads = await piClient.listThreads({
|
|
357
|
-
...(workspacePath ? { workspacePath } : {}),
|
|
358
|
-
includeArchived,
|
|
359
|
-
});
|
|
360
|
-
return Response.json(threads);
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
export const POST = withFail(async (req: NextRequest) => {
|
|
364
|
-
const body = await req.json().catch(() => ({}));
|
|
365
|
-
return Response.json(await piClient.createThread(body));
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
## app/globals.css
|
|
371
|
-
|
|
372
|
-
```css
|
|
373
|
-
@import "tailwindcss";
|
|
374
|
-
@import "tw-animate-css";
|
|
375
|
-
|
|
376
|
-
@custom-variant data-open (&:where([data-state="open"], [data-open]:not([data-open="false"])));
|
|
377
|
-
@custom-variant data-closed (&:where([data-state="closed"], [data-closed]:not([data-closed="false"])));
|
|
378
|
-
|
|
379
|
-
@theme inline {
|
|
380
|
-
@keyframes collapsible-down {
|
|
381
|
-
from {
|
|
382
|
-
height: 0;
|
|
383
|
-
}
|
|
384
|
-
to {
|
|
385
|
-
height: var(
|
|
386
|
-
--radix-collapsible-content-height,
|
|
387
|
-
var(--collapsible-panel-height, auto)
|
|
388
|
-
);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
@keyframes collapsible-up {
|
|
392
|
-
from {
|
|
393
|
-
height: var(
|
|
394
|
-
--radix-collapsible-content-height,
|
|
395
|
-
var(--collapsible-panel-height, auto)
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
to {
|
|
399
|
-
height: 0;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
@source "../../../packages/ui/src";
|
|
405
|
-
|
|
406
|
-
@custom-variant dark (&:is(.dark *));
|
|
407
|
-
|
|
408
|
-
@theme inline {
|
|
409
|
-
--radius-sm: calc(var(--radius) - 4px);
|
|
410
|
-
--radius-md: calc(var(--radius) - 2px);
|
|
411
|
-
--radius-lg: var(--radius);
|
|
412
|
-
--radius-xl: calc(var(--radius) + 4px);
|
|
413
|
-
--color-background: var(--background);
|
|
414
|
-
--color-foreground: var(--foreground);
|
|
415
|
-
--color-card: var(--card);
|
|
416
|
-
--color-card-foreground: var(--card-foreground);
|
|
417
|
-
--color-popover: var(--popover);
|
|
418
|
-
--color-popover-foreground: var(--popover-foreground);
|
|
419
|
-
--color-primary: var(--primary);
|
|
420
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
421
|
-
--color-secondary: var(--secondary);
|
|
422
|
-
--color-secondary-foreground: var(--secondary-foreground);
|
|
423
|
-
--color-muted: var(--muted);
|
|
424
|
-
--color-muted-foreground: var(--muted-foreground);
|
|
425
|
-
--color-accent: var(--accent);
|
|
426
|
-
--color-accent-foreground: var(--accent-foreground);
|
|
427
|
-
--color-destructive: var(--destructive);
|
|
428
|
-
--color-border: var(--border);
|
|
429
|
-
--color-input: var(--input);
|
|
430
|
-
--color-ring: var(--ring);
|
|
431
|
-
--color-chart-1: var(--chart-1);
|
|
432
|
-
--color-chart-2: var(--chart-2);
|
|
433
|
-
--color-chart-3: var(--chart-3);
|
|
434
|
-
--color-chart-4: var(--chart-4);
|
|
435
|
-
--color-chart-5: var(--chart-5);
|
|
436
|
-
--color-sidebar: var(--sidebar);
|
|
437
|
-
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
438
|
-
--color-sidebar-primary: var(--sidebar-primary);
|
|
439
|
-
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
440
|
-
--color-sidebar-accent: var(--sidebar-accent);
|
|
441
|
-
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
442
|
-
--color-sidebar-border: var(--sidebar-border);
|
|
443
|
-
--color-sidebar-ring: var(--sidebar-ring);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
:root {
|
|
447
|
-
--radius: 0.625rem;
|
|
448
|
-
--background: oklch(1 0 0);
|
|
449
|
-
--foreground: oklch(0.141 0.005 285.823);
|
|
450
|
-
--card: oklch(1 0 0);
|
|
451
|
-
--card-foreground: oklch(0.141 0.005 285.823);
|
|
452
|
-
--popover: oklch(1 0 0);
|
|
453
|
-
--popover-foreground: oklch(0.141 0.005 285.823);
|
|
454
|
-
--primary: oklch(0.21 0.006 285.885);
|
|
455
|
-
--primary-foreground: oklch(0.985 0 0);
|
|
456
|
-
--secondary: oklch(0.967 0.001 286.375);
|
|
457
|
-
--secondary-foreground: oklch(0.21 0.006 285.885);
|
|
458
|
-
--muted: oklch(0.967 0.001 286.375);
|
|
459
|
-
--muted-foreground: oklch(0.552 0.016 285.938);
|
|
460
|
-
--accent: oklch(0.967 0.001 286.375);
|
|
461
|
-
--accent-foreground: oklch(0.21 0.006 285.885);
|
|
462
|
-
--destructive: oklch(0.577 0.245 27.325);
|
|
463
|
-
--border: oklch(0.92 0.004 286.32);
|
|
464
|
-
--input: oklch(0.92 0.004 286.32);
|
|
465
|
-
--ring: oklch(0.705 0.015 286.067);
|
|
466
|
-
--chart-1: oklch(0.646 0.222 41.116);
|
|
467
|
-
--chart-2: oklch(0.6 0.118 184.704);
|
|
468
|
-
--chart-3: oklch(0.398 0.07 227.392);
|
|
469
|
-
--chart-4: oklch(0.828 0.189 84.429);
|
|
470
|
-
--chart-5: oklch(0.769 0.188 70.08);
|
|
471
|
-
--sidebar: oklch(0.985 0 0);
|
|
472
|
-
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
|
473
|
-
--sidebar-primary: oklch(0.21 0.006 285.885);
|
|
474
|
-
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
475
|
-
--sidebar-accent: oklch(0.967 0.001 286.375);
|
|
476
|
-
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
|
477
|
-
--sidebar-border: oklch(0.92 0.004 286.32);
|
|
478
|
-
--sidebar-ring: oklch(0.705 0.015 286.067);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
.dark {
|
|
482
|
-
--background: oklch(0.141 0.005 285.823);
|
|
483
|
-
--foreground: oklch(0.985 0 0);
|
|
484
|
-
--card: oklch(0.21 0.006 285.885);
|
|
485
|
-
--card-foreground: oklch(0.985 0 0);
|
|
486
|
-
--popover: oklch(0.21 0.006 285.885);
|
|
487
|
-
--popover-foreground: oklch(0.985 0 0);
|
|
488
|
-
--primary: oklch(0.92 0.004 286.32);
|
|
489
|
-
--primary-foreground: oklch(0.21 0.006 285.885);
|
|
490
|
-
--secondary: oklch(0.274 0.006 286.033);
|
|
491
|
-
--secondary-foreground: oklch(0.985 0 0);
|
|
492
|
-
--muted: oklch(0.274 0.006 286.033);
|
|
493
|
-
--muted-foreground: oklch(0.705 0.015 286.067);
|
|
494
|
-
--accent: oklch(0.274 0.006 286.033);
|
|
495
|
-
--accent-foreground: oklch(0.985 0 0);
|
|
496
|
-
--destructive: oklch(0.704 0.191 22.216);
|
|
497
|
-
--border: oklch(1 0 0 / 10%);
|
|
498
|
-
--input: oklch(1 0 0 / 15%);
|
|
499
|
-
--ring: oklch(0.552 0.016 285.938);
|
|
500
|
-
--chart-1: oklch(0.488 0.243 264.376);
|
|
501
|
-
--chart-2: oklch(0.696 0.17 162.48);
|
|
502
|
-
--chart-3: oklch(0.769 0.188 70.08);
|
|
503
|
-
--chart-4: oklch(0.627 0.265 303.9);
|
|
504
|
-
--chart-5: oklch(0.645 0.246 16.439);
|
|
505
|
-
--sidebar: oklch(0.21 0.006 285.885);
|
|
506
|
-
--sidebar-foreground: oklch(0.985 0 0);
|
|
507
|
-
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
508
|
-
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
509
|
-
--sidebar-accent: oklch(0.274 0.006 286.033);
|
|
510
|
-
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
511
|
-
--sidebar-border: oklch(1 0 0 / 10%);
|
|
512
|
-
--sidebar-ring: oklch(0.552 0.016 285.938);
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
@layer base {
|
|
516
|
-
* {
|
|
517
|
-
@apply border-border outline-ring/50;
|
|
518
|
-
}
|
|
519
|
-
body {
|
|
520
|
-
@apply bg-background text-foreground;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
## app/layout.tsx
|
|
527
|
-
|
|
528
|
-
```tsx
|
|
529
|
-
import "./globals.css";
|
|
530
|
-
import type { Metadata } from "next";
|
|
531
|
-
import type { ReactNode } from "react";
|
|
532
|
-
|
|
533
|
-
export const metadata: Metadata = {
|
|
534
|
-
title: "assistant-ui × Pi",
|
|
535
|
-
description: "Pi coding-agent runtime adapter for assistant-ui",
|
|
536
|
-
};
|
|
537
|
-
|
|
538
|
-
export default function RootLayout({ children }: { children: ReactNode }) {
|
|
539
|
-
return (
|
|
540
|
-
<html lang="en" className="h-dvh">
|
|
541
|
-
<body className="h-dvh overflow-hidden">{children}</body>
|
|
542
|
-
</html>
|
|
543
|
-
);
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
## app/page.tsx
|
|
549
|
-
|
|
550
|
-
```tsx
|
|
551
|
-
"use client";
|
|
552
|
-
|
|
553
|
-
import { useEffect, useState } from "react";
|
|
554
|
-
import { Thread } from "../components/assistant-ui/thread";
|
|
555
|
-
import { ThreadList } from "@/components/assistant-ui/thread-list";
|
|
556
|
-
import { usePiThreadState } from "@assistant-ui/react-pi";
|
|
557
|
-
import type { PiRuntimeReadiness } from "@assistant-ui/react-pi";
|
|
558
|
-
// Type-only import — erased at build time, so no server code reaches the client.
|
|
559
|
-
import type { PiHandshake } from "@/lib/pi-server";
|
|
560
|
-
import { PiHandshakeProvider } from "../components/pi-handshake";
|
|
561
|
-
import { PiRuntimeProvider } from "./PiRuntimeProvider";
|
|
562
|
-
import { WorkspaceBrowser } from "../components/workspace-browser";
|
|
563
|
-
|
|
564
|
-
export default function Home() {
|
|
565
|
-
const [handshake, setHandshake] = useState<PiHandshake | null>(null);
|
|
566
|
-
const [workspacePath, setWorkspacePath] = useState("");
|
|
567
|
-
|
|
568
|
-
useEffect(() => {
|
|
569
|
-
let active = true;
|
|
570
|
-
fetch("/api/pi/handshake")
|
|
571
|
-
.then((response) => response.json())
|
|
572
|
-
.then((data: PiHandshake) => {
|
|
573
|
-
if (!active) return;
|
|
574
|
-
setHandshake(data);
|
|
575
|
-
setWorkspacePath(data.workspacePath ?? "");
|
|
576
|
-
})
|
|
577
|
-
.catch(() => {
|
|
578
|
-
/* handshake is best-effort; the runtime still works on env defaults. */
|
|
579
|
-
});
|
|
580
|
-
return () => {
|
|
581
|
-
active = false;
|
|
582
|
-
};
|
|
583
|
-
}, []);
|
|
584
|
-
|
|
585
|
-
return (
|
|
586
|
-
<PiRuntimeProvider workspacePath={workspacePath || undefined}>
|
|
587
|
-
<PiHandshakeProvider value={handshake}>
|
|
588
|
-
<div className="flex h-dvh flex-col overflow-hidden">
|
|
589
|
-
<Header
|
|
590
|
-
workspacePath={workspacePath}
|
|
591
|
-
onCommitWorkspace={setWorkspacePath}
|
|
592
|
-
/>
|
|
593
|
-
<ReadinessBanner fallback={handshake?.readiness} />
|
|
594
|
-
<div className="flex min-h-0 flex-grow">
|
|
595
|
-
<aside className="flex w-72 shrink-0 flex-col overflow-hidden border-r">
|
|
596
|
-
<div className="flex-1 overflow-y-auto p-3">
|
|
597
|
-
<ThreadList />
|
|
598
|
-
</div>
|
|
599
|
-
</aside>
|
|
600
|
-
<main className="flex min-h-0 min-w-0 flex-grow flex-col overflow-hidden">
|
|
601
|
-
<Thread />
|
|
602
|
-
</main>
|
|
603
|
-
</div>
|
|
604
|
-
</div>
|
|
605
|
-
</PiHandshakeProvider>
|
|
606
|
-
</PiRuntimeProvider>
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
function Header({
|
|
611
|
-
workspacePath,
|
|
612
|
-
onCommitWorkspace,
|
|
613
|
-
}: {
|
|
614
|
-
workspacePath: string;
|
|
615
|
-
onCommitWorkspace: (value: string) => void;
|
|
616
|
-
}) {
|
|
617
|
-
return (
|
|
618
|
-
<header className="flex items-center gap-3 border-b px-4 py-2">
|
|
619
|
-
<span className="font-semibold">assistant-ui × Pi</span>
|
|
620
|
-
|
|
621
|
-
<div className="ml-auto flex items-center gap-2">
|
|
622
|
-
<WorkspaceBrowser value={workspacePath} onCommit={onCommitWorkspace} />
|
|
623
|
-
</div>
|
|
624
|
-
</header>
|
|
625
|
-
);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
function ReadinessBanner({
|
|
629
|
-
fallback,
|
|
630
|
-
}: {
|
|
631
|
-
fallback?: PiRuntimeReadiness | undefined;
|
|
632
|
-
}) {
|
|
633
|
-
const live = usePiThreadState((state) => state.readiness);
|
|
634
|
-
const readiness = live ?? fallback;
|
|
635
|
-
if (!readiness || readiness.state === "ready") return null;
|
|
636
|
-
return (
|
|
637
|
-
<div className="bg-destructive/10 text-destructive border-destructive/20 border-b px-4 py-2 text-sm">
|
|
638
|
-
{readiness.message ?? "The Pi runtime has no usable model selected."}
|
|
639
|
-
</div>
|
|
640
|
-
);
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
```
|
|
644
|
-
|
|
645
|
-
## app/PiRuntimeProvider.tsx
|
|
646
|
-
|
|
647
|
-
```tsx
|
|
648
|
-
"use client";
|
|
649
|
-
|
|
650
|
-
import {
|
|
651
|
-
AssistantRuntimeProvider,
|
|
652
|
-
SimpleImageAttachmentAdapter,
|
|
653
|
-
} from "@assistant-ui/react";
|
|
654
|
-
import { createPiHttpClient, usePiRuntime } from "@assistant-ui/react-pi";
|
|
655
|
-
import { useMemo, type ReactNode } from "react";
|
|
656
|
-
|
|
657
|
-
/**
|
|
658
|
-
* Wires the browser `PiClient` (HTTP/SSE over `/api/pi`) into a Pi runtime. The
|
|
659
|
-
* client is stable for the app's lifetime; `workspacePath` scopes the thread
|
|
660
|
-
* list and seeds new sessions. Image attachments ride along as Pi
|
|
661
|
-
* `ImageContent` (the adapter's data URLs pass through `buildPiSendInput`).
|
|
662
|
-
*/
|
|
663
|
-
export function PiRuntimeProvider({
|
|
664
|
-
workspacePath,
|
|
665
|
-
children,
|
|
666
|
-
}: {
|
|
667
|
-
workspacePath?: string | undefined;
|
|
668
|
-
children: ReactNode;
|
|
669
|
-
}) {
|
|
670
|
-
const client = useMemo(() => createPiHttpClient(), []);
|
|
671
|
-
const adapters = useMemo(
|
|
672
|
-
() => ({ attachments: new SimpleImageAttachmentAdapter() }),
|
|
673
|
-
[],
|
|
674
|
-
);
|
|
675
|
-
const runtime = usePiRuntime({
|
|
676
|
-
client,
|
|
677
|
-
adapters,
|
|
678
|
-
...(workspacePath ? { workspacePath } : {}),
|
|
679
|
-
});
|
|
680
|
-
|
|
681
|
-
return (
|
|
682
|
-
<AssistantRuntimeProvider runtime={runtime}>
|
|
683
|
-
{children}
|
|
684
|
-
</AssistantRuntimeProvider>
|
|
685
|
-
);
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
```
|
|
689
|
-
|
|
690
|
-
## components.json
|
|
691
|
-
|
|
692
|
-
```json
|
|
693
|
-
{
|
|
694
|
-
"$schema": "https://ui.shadcn.com/schema.json",
|
|
695
|
-
"style": "new-york",
|
|
696
|
-
"rsc": true,
|
|
697
|
-
"tsx": true,
|
|
698
|
-
"tailwind": {
|
|
699
|
-
"config": "",
|
|
700
|
-
"css": "app/globals.css",
|
|
701
|
-
"baseColor": "zinc",
|
|
702
|
-
"cssVariables": true,
|
|
703
|
-
"prefix": ""
|
|
704
|
-
},
|
|
705
|
-
"aliases": {
|
|
706
|
-
"components": "@/components",
|
|
707
|
-
"utils": "@/lib/utils",
|
|
708
|
-
"ui": "@/components/ui",
|
|
709
|
-
"lib": "@/lib",
|
|
710
|
-
"hooks": "@/hooks"
|
|
711
|
-
},
|
|
712
|
-
"iconLibrary": "lucide",
|
|
713
|
-
"registries": {
|
|
714
|
-
"@assistant-ui": "https://r.assistant-ui.com/{name}.json"
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
## components/assistant-ui/thinking-level-slider.tsx
|
|
721
|
-
|
|
722
|
-
```tsx
|
|
723
|
-
"use client";
|
|
724
|
-
|
|
725
|
-
import type { KeyboardEvent } from "react";
|
|
726
|
-
import { useModelSelectorEfforts } from "@/components/assistant-ui/model-selector";
|
|
727
|
-
import {
|
|
728
|
-
Tooltip,
|
|
729
|
-
TooltipContent,
|
|
730
|
-
TooltipProvider,
|
|
731
|
-
TooltipTrigger,
|
|
732
|
-
} from "@/components/ui/radix/tooltip";
|
|
733
|
-
import { cn } from "@/lib/utils";
|
|
734
|
-
|
|
735
|
-
/**
|
|
736
|
-
* Line-graph style thinking level picker, composed into ModelSelector.Content
|
|
737
|
-
* via the useModelSelectorEfforts hook. Pi models expose up to six levels,
|
|
738
|
-
* which overflow the built-in horizontal ModelSelector.Effort layout — here
|
|
739
|
-
* each level is a thin vertical line that grows on hover and names itself in
|
|
740
|
-
* a tooltip, with the active level standing taller in the accent color.
|
|
741
|
-
*/
|
|
742
|
-
export const ThinkingLevelSlider = ({
|
|
743
|
-
label = "Thinking",
|
|
744
|
-
}: {
|
|
745
|
-
label?: string;
|
|
746
|
-
}) => {
|
|
747
|
-
const { efforts, effort, setEffort } = useModelSelectorEfforts();
|
|
748
|
-
|
|
749
|
-
if (!efforts?.length) return null;
|
|
750
|
-
|
|
751
|
-
const activeIndex = efforts.findIndex((e) => e.id === effort);
|
|
752
|
-
const lastIndex = efforts.length - 1;
|
|
753
|
-
|
|
754
|
-
const selectIndex = (event: KeyboardEvent<HTMLDivElement>, index: number) => {
|
|
755
|
-
const target = Math.min(Math.max(index, 0), lastIndex);
|
|
756
|
-
const next = efforts[target];
|
|
757
|
-
if (!next) return;
|
|
758
|
-
event.preventDefault();
|
|
759
|
-
setEffort(next.id);
|
|
760
|
-
event.currentTarget
|
|
761
|
-
.querySelectorAll<HTMLElement>('[role="radio"]')
|
|
762
|
-
[target]?.focus();
|
|
763
|
-
};
|
|
764
|
-
|
|
765
|
-
return (
|
|
766
|
-
<div className="flex items-center justify-between gap-3 border-t px-3 py-2">
|
|
767
|
-
<span className="text-muted-foreground text-xs">{label}</span>
|
|
768
|
-
<TooltipProvider>
|
|
769
|
-
<div
|
|
770
|
-
role="radiogroup"
|
|
771
|
-
aria-label={label}
|
|
772
|
-
className="flex items-end"
|
|
773
|
-
onKeyDown={(event) => {
|
|
774
|
-
if (event.key === "ArrowRight" || event.key === "ArrowUp") {
|
|
775
|
-
selectIndex(event, activeIndex + 1);
|
|
776
|
-
} else if (event.key === "ArrowLeft" || event.key === "ArrowDown") {
|
|
777
|
-
selectIndex(event, activeIndex - 1);
|
|
778
|
-
} else if (event.key === "Home") {
|
|
779
|
-
selectIndex(event, 0);
|
|
780
|
-
} else if (event.key === "End") {
|
|
781
|
-
selectIndex(event, lastIndex);
|
|
782
|
-
}
|
|
783
|
-
}}
|
|
784
|
-
>
|
|
785
|
-
{efforts.map((option, index) => {
|
|
786
|
-
const isActive = index === activeIndex;
|
|
787
|
-
return (
|
|
788
|
-
<Tooltip key={option.id} disableHoverableContent>
|
|
789
|
-
<TooltipTrigger asChild>
|
|
790
|
-
<button
|
|
791
|
-
type="button"
|
|
792
|
-
role="radio"
|
|
793
|
-
aria-checked={isActive}
|
|
794
|
-
aria-label={option.name}
|
|
795
|
-
tabIndex={index === Math.max(activeIndex, 0) ? 0 : -1}
|
|
796
|
-
data-state={isActive ? "on" : "off"}
|
|
797
|
-
onClick={() => setEffort(option.id)}
|
|
798
|
-
className="group focus-visible:ring-ring/50 flex h-6 w-3.5 items-end justify-center rounded-sm outline-none focus-visible:ring-2"
|
|
799
|
-
>
|
|
800
|
-
<span
|
|
801
|
-
className={cn(
|
|
802
|
-
"rounded-full transition-all duration-150 ease-out",
|
|
803
|
-
isActive
|
|
804
|
-
? "bg-primary h-4.5 w-0.5"
|
|
805
|
-
: "bg-muted-foreground/40 group-hover:bg-foreground/70 h-2.5 w-0.5 group-hover:h-4 group-hover:w-[3px] group-focus-visible:h-4 group-focus-visible:w-[3px]",
|
|
806
|
-
)}
|
|
807
|
-
/>
|
|
808
|
-
</button>
|
|
809
|
-
</TooltipTrigger>
|
|
810
|
-
<TooltipContent side="top" sideOffset={2}>
|
|
811
|
-
{option.name}
|
|
812
|
-
</TooltipContent>
|
|
813
|
-
</Tooltip>
|
|
814
|
-
);
|
|
815
|
-
})}
|
|
816
|
-
</div>
|
|
817
|
-
</TooltipProvider>
|
|
818
|
-
<span className="text-foreground min-w-12 text-end text-xs">
|
|
819
|
-
{efforts[activeIndex]?.name}
|
|
820
|
-
</span>
|
|
821
|
-
</div>
|
|
822
|
-
);
|
|
823
|
-
};
|
|
824
|
-
|
|
825
|
-
```
|
|
826
|
-
|
|
827
|
-
## components/assistant-ui/thread.tsx
|
|
828
|
-
|
|
829
|
-
```tsx
|
|
830
|
-
"use client";
|
|
831
|
-
|
|
832
|
-
import {
|
|
833
|
-
ComposerAddAttachment,
|
|
834
|
-
ComposerAttachments,
|
|
835
|
-
UserMessageAttachments,
|
|
836
|
-
} from "@/components/assistant-ui/attachment";
|
|
837
|
-
import { ContextDisplay } from "@/components/assistant-ui/context-display";
|
|
838
|
-
import { ModelSelector } from "@/components/assistant-ui/model-selector";
|
|
839
|
-
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
|
|
840
|
-
import {
|
|
841
|
-
isPiSteerQueueItemId,
|
|
842
|
-
responseForRequest,
|
|
843
|
-
usePiHostUiRequests,
|
|
844
|
-
usePiRuntimeExtras,
|
|
845
|
-
usePiThreadState,
|
|
846
|
-
type PiHostUiRequest,
|
|
847
|
-
type PiThinkingLevel,
|
|
848
|
-
} from "@assistant-ui/react-pi";
|
|
849
|
-
import { usePiHandshake } from "../pi-handshake";
|
|
850
|
-
import { modelKey } from "@/lib/model-key";
|
|
851
|
-
import { ThinkingLevelSlider } from "./thinking-level-slider";
|
|
852
|
-
import {
|
|
853
|
-
Reasoning,
|
|
854
|
-
ReasoningContent,
|
|
855
|
-
ReasoningRoot,
|
|
856
|
-
ReasoningText,
|
|
857
|
-
ReasoningTrigger,
|
|
858
|
-
} from "@/components/assistant-ui/reasoning";
|
|
859
|
-
import {
|
|
860
|
-
ToolGroupContent,
|
|
861
|
-
ToolGroupRoot,
|
|
862
|
-
ToolGroupTrigger,
|
|
863
|
-
} from "@/components/assistant-ui/tool-group";
|
|
864
|
-
import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
|
|
865
|
-
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
866
|
-
import { Button } from "@/components/ui/button";
|
|
867
|
-
import { cn } from "@/lib/utils";
|
|
868
|
-
import {
|
|
869
|
-
ActionBarMorePrimitive,
|
|
870
|
-
ActionBarPrimitive,
|
|
871
|
-
AuiIf,
|
|
872
|
-
BranchPickerPrimitive,
|
|
873
|
-
ComposerPrimitive,
|
|
874
|
-
ErrorPrimitive,
|
|
875
|
-
groupPartByType,
|
|
876
|
-
MessagePrimitive,
|
|
877
|
-
SuggestionPrimitive,
|
|
878
|
-
ThreadPrimitive,
|
|
879
|
-
useAui,
|
|
880
|
-
useAuiState,
|
|
881
|
-
} from "@assistant-ui/react";
|
|
882
|
-
import {
|
|
883
|
-
ArrowDownIcon,
|
|
884
|
-
ArrowUpIcon,
|
|
885
|
-
CheckIcon,
|
|
886
|
-
ChevronLeftIcon,
|
|
887
|
-
ChevronRightIcon,
|
|
888
|
-
CopyIcon,
|
|
889
|
-
DownloadIcon,
|
|
890
|
-
ListEndIcon,
|
|
891
|
-
MoreHorizontalIcon,
|
|
892
|
-
PencilIcon,
|
|
893
|
-
RefreshCwIcon,
|
|
894
|
-
SquareIcon,
|
|
895
|
-
Trash2Icon,
|
|
896
|
-
WrenchIcon,
|
|
897
|
-
} from "lucide-react";
|
|
898
|
-
import { useState, type FC } from "react";
|
|
899
|
-
|
|
900
|
-
export const Thread: FC = () => {
|
|
901
|
-
return (
|
|
902
|
-
<ThreadPrimitive.Root
|
|
903
|
-
className="aui-root aui-thread-root bg-background @container flex h-full flex-col"
|
|
904
|
-
style={{
|
|
905
|
-
["--thread-max-width" as string]: "44rem",
|
|
906
|
-
["--composer-radius" as string]: "24px",
|
|
907
|
-
["--composer-padding" as string]: "10px",
|
|
908
|
-
}}
|
|
909
|
-
>
|
|
910
|
-
<ThreadPrimitive.Viewport
|
|
911
|
-
data-slot="aui_thread-viewport"
|
|
912
|
-
className="relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll scroll-smooth"
|
|
913
|
-
>
|
|
914
|
-
<div className="mx-auto flex w-full max-w-(--thread-max-width) flex-1 flex-col px-4 pt-4">
|
|
915
|
-
<AuiIf condition={(s) => s.thread.isEmpty}>
|
|
916
|
-
<ThreadWelcome />
|
|
917
|
-
</AuiIf>
|
|
918
|
-
|
|
919
|
-
<div
|
|
920
|
-
data-slot="aui_message-group"
|
|
921
|
-
className="mb-10 flex flex-col gap-y-8 empty:hidden"
|
|
922
|
-
>
|
|
923
|
-
<ThreadPrimitive.Messages>
|
|
924
|
-
{() => <ThreadMessage />}
|
|
925
|
-
</ThreadPrimitive.Messages>
|
|
926
|
-
</div>
|
|
927
|
-
|
|
928
|
-
<ThreadPrimitive.ViewportFooter className="aui-thread-viewport-footer bg-background sticky bottom-0 mt-auto flex flex-col gap-4 overflow-visible rounded-t-(--composer-radius) pb-4 md:pb-6">
|
|
929
|
-
<ThreadScrollToBottom />
|
|
930
|
-
<HostUiRequests />
|
|
931
|
-
<Composer />
|
|
932
|
-
</ThreadPrimitive.ViewportFooter>
|
|
933
|
-
</div>
|
|
934
|
-
</ThreadPrimitive.Viewport>
|
|
935
|
-
</ThreadPrimitive.Root>
|
|
936
|
-
);
|
|
937
|
-
};
|
|
938
|
-
|
|
939
|
-
const ThreadMessage: FC = () => {
|
|
940
|
-
const role = useAuiState((s) => s.message.role);
|
|
941
|
-
const isEditing = useAuiState((s) => s.message.composer.isEditing);
|
|
942
|
-
|
|
943
|
-
if (isEditing) return <EditComposer />;
|
|
944
|
-
if (role === "user") return <UserMessage />;
|
|
945
|
-
return <AssistantMessage />;
|
|
946
|
-
};
|
|
947
|
-
|
|
948
|
-
const ThreadScrollToBottom: FC = () => {
|
|
949
|
-
return (
|
|
950
|
-
<ThreadPrimitive.ScrollToBottom asChild>
|
|
951
|
-
<TooltipIconButton
|
|
952
|
-
tooltip="Scroll to bottom"
|
|
953
|
-
variant="outline"
|
|
954
|
-
className="aui-thread-scroll-to-bottom dark:border-border dark:bg-background dark:hover:bg-accent absolute -top-12 z-10 self-center rounded-full p-4 disabled:invisible"
|
|
955
|
-
>
|
|
956
|
-
<ArrowDownIcon />
|
|
957
|
-
</TooltipIconButton>
|
|
958
|
-
</ThreadPrimitive.ScrollToBottom>
|
|
959
|
-
);
|
|
960
|
-
};
|
|
961
|
-
|
|
962
|
-
const ThreadWelcome: FC = () => {
|
|
963
|
-
return (
|
|
964
|
-
<div className="aui-thread-welcome-root my-auto flex grow flex-col">
|
|
965
|
-
<div className="aui-thread-welcome-center flex w-full grow flex-col items-center justify-center">
|
|
966
|
-
<div className="aui-thread-welcome-message flex size-full flex-col justify-center px-4">
|
|
967
|
-
<h1 className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in fill-mode-both text-2xl font-semibold duration-200">
|
|
968
|
-
Hello there!
|
|
969
|
-
</h1>
|
|
970
|
-
<p className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in fill-mode-both text-muted-foreground text-xl delay-75 duration-200">
|
|
971
|
-
How can I help you today?
|
|
972
|
-
</p>
|
|
973
|
-
</div>
|
|
974
|
-
</div>
|
|
975
|
-
<ThreadSuggestions />
|
|
976
|
-
</div>
|
|
977
|
-
);
|
|
978
|
-
};
|
|
979
|
-
|
|
980
|
-
const ThreadSuggestions: FC = () => {
|
|
981
|
-
return (
|
|
982
|
-
<div className="aui-thread-welcome-suggestions grid w-full gap-2 pb-4 @md:grid-cols-2">
|
|
983
|
-
<ThreadPrimitive.Suggestions>
|
|
984
|
-
{() => <ThreadSuggestionItem />}
|
|
985
|
-
</ThreadPrimitive.Suggestions>
|
|
986
|
-
</div>
|
|
987
|
-
);
|
|
988
|
-
};
|
|
989
|
-
|
|
990
|
-
const ThreadSuggestionItem: FC = () => {
|
|
991
|
-
return (
|
|
992
|
-
<div className="aui-thread-welcome-suggestion-display fade-in slide-in-from-bottom-2 animate-in fill-mode-both duration-200 nth-[n+3]:hidden @md:nth-[n+3]:block">
|
|
993
|
-
<SuggestionPrimitive.Trigger send asChild>
|
|
994
|
-
<Button
|
|
995
|
-
variant="ghost"
|
|
996
|
-
className="aui-thread-welcome-suggestion bg-background hover:bg-muted h-auto w-full flex-wrap items-start justify-start gap-1 rounded-3xl border px-4 py-3 text-start text-sm transition-colors @md:flex-col"
|
|
997
|
-
>
|
|
998
|
-
<SuggestionPrimitive.Title className="aui-thread-welcome-suggestion-text-1 font-medium" />
|
|
999
|
-
<SuggestionPrimitive.Description className="aui-thread-welcome-suggestion-text-2 text-muted-foreground empty:hidden" />
|
|
1000
|
-
</Button>
|
|
1001
|
-
</SuggestionPrimitive.Trigger>
|
|
1002
|
-
</div>
|
|
1003
|
-
);
|
|
1004
|
-
};
|
|
1005
|
-
|
|
1006
|
-
const HostUiRequests: FC = () => {
|
|
1007
|
-
const { requests, respond } = usePiHostUiRequests();
|
|
1008
|
-
const request = requests[0];
|
|
1009
|
-
if (!request) return null;
|
|
1010
|
-
return (
|
|
1011
|
-
<HostUiRequestCard key={request.id} request={request} respond={respond} />
|
|
1012
|
-
);
|
|
1013
|
-
};
|
|
1014
|
-
|
|
1015
|
-
const HostUiRequestCard: FC<{
|
|
1016
|
-
request: PiHostUiRequest;
|
|
1017
|
-
respond: ReturnType<typeof usePiHostUiRequests>["respond"];
|
|
1018
|
-
}> = ({ request, respond }) => {
|
|
1019
|
-
const [value, setValue] = useState(
|
|
1020
|
-
request.kind === "editor" ? (request.prefill ?? "") : "",
|
|
1021
|
-
);
|
|
1022
|
-
|
|
1023
|
-
const submitValue = () => {
|
|
1024
|
-
void respond(responseForRequest(request, value));
|
|
1025
|
-
};
|
|
1026
|
-
|
|
1027
|
-
return (
|
|
1028
|
-
<div className="border-border bg-background mx-2 rounded-2xl border p-3 text-sm shadow-sm">
|
|
1029
|
-
<div className="font-medium">{request.title}</div>
|
|
1030
|
-
{request.kind === "confirm" ? (
|
|
1031
|
-
<p className="text-muted-foreground mt-1">{request.message}</p>
|
|
1032
|
-
) : request.kind === "select" ? (
|
|
1033
|
-
<div className="mt-2 flex flex-wrap gap-2">
|
|
1034
|
-
{request.options.map((option) => (
|
|
1035
|
-
<Button
|
|
1036
|
-
key={option}
|
|
1037
|
-
size="sm"
|
|
1038
|
-
variant="outline"
|
|
1039
|
-
onClick={() => void respond(responseForRequest(request, option))}
|
|
1040
|
-
>
|
|
1041
|
-
{option}
|
|
1042
|
-
</Button>
|
|
1043
|
-
))}
|
|
1044
|
-
</div>
|
|
1045
|
-
) : request.kind === "editor" ? (
|
|
1046
|
-
<textarea
|
|
1047
|
-
value={value}
|
|
1048
|
-
onChange={(event) => setValue(event.currentTarget.value)}
|
|
1049
|
-
className="border-input mt-2 min-h-24 w-full rounded-md border bg-transparent p-2 font-mono text-xs"
|
|
1050
|
-
/>
|
|
1051
|
-
) : (
|
|
1052
|
-
<input
|
|
1053
|
-
value={value}
|
|
1054
|
-
placeholder={request.placeholder}
|
|
1055
|
-
onChange={(event) => setValue(event.currentTarget.value)}
|
|
1056
|
-
className="border-input mt-2 w-full rounded-md border bg-transparent px-2 py-1"
|
|
1057
|
-
/>
|
|
1058
|
-
)}
|
|
1059
|
-
<div className="mt-3 flex justify-end gap-2">
|
|
1060
|
-
<Button
|
|
1061
|
-
size="sm"
|
|
1062
|
-
variant="ghost"
|
|
1063
|
-
onClick={() =>
|
|
1064
|
-
void respond(
|
|
1065
|
-
request.kind === "confirm"
|
|
1066
|
-
? responseForRequest(request, false)
|
|
1067
|
-
: { requestId: request.id, dismissed: true },
|
|
1068
|
-
)
|
|
1069
|
-
}
|
|
1070
|
-
>
|
|
1071
|
-
{request.kind === "confirm" ? "Deny" : "Dismiss"}
|
|
1072
|
-
</Button>
|
|
1073
|
-
{request.kind !== "select" && (
|
|
1074
|
-
<Button
|
|
1075
|
-
size="sm"
|
|
1076
|
-
onClick={() =>
|
|
1077
|
-
request.kind === "confirm"
|
|
1078
|
-
? void respond(responseForRequest(request, true))
|
|
1079
|
-
: submitValue()
|
|
1080
|
-
}
|
|
1081
|
-
>
|
|
1082
|
-
{request.kind === "confirm" ? "Approve" : "Submit"}
|
|
1083
|
-
</Button>
|
|
1084
|
-
)}
|
|
1085
|
-
</div>
|
|
1086
|
-
</div>
|
|
1087
|
-
);
|
|
1088
|
-
};
|
|
1089
|
-
|
|
1090
|
-
/** Mid-run sends sit in Pi's queue (not the thread); this card mirrors them
|
|
1091
|
-
* above the composer. Pi only supports clearing the whole queue — no per-item
|
|
1092
|
-
* remove/promote — so the lone action restores all queued text to the composer. */
|
|
1093
|
-
const ComposerQueue: FC = () => {
|
|
1094
|
-
const aui = useAui();
|
|
1095
|
-
const { clearQueue } = usePiRuntimeExtras();
|
|
1096
|
-
const queueLength = useAuiState((s) => s.composer.queue.length);
|
|
1097
|
-
if (queueLength === 0) return null;
|
|
1098
|
-
|
|
1099
|
-
const handleClear = () => {
|
|
1100
|
-
clearQueue()
|
|
1101
|
-
.then(({ steering, followUp }) => {
|
|
1102
|
-
const restored = [...steering, ...followUp].join("\n");
|
|
1103
|
-
if (!restored) return;
|
|
1104
|
-
const composer = aui.composer;
|
|
1105
|
-
const current = composer.getState().text;
|
|
1106
|
-
composer.setText(current ? `${current}\n${restored}` : restored);
|
|
1107
|
-
})
|
|
1108
|
-
.catch((error: unknown) => console.error("Failed to clear queue", error));
|
|
1109
|
-
};
|
|
1110
|
-
|
|
1111
|
-
return (
|
|
1112
|
-
<div className="bg-muted/50 border-border/60 text-muted-foreground -mb-4 flex flex-col gap-1.5 rounded-t-(--composer-radius) border border-b-0 px-4 pt-2.5 pb-7 text-sm">
|
|
1113
|
-
<div className="flex items-center justify-between gap-2">
|
|
1114
|
-
<span className="text-xs font-medium">Queued</span>
|
|
1115
|
-
<Button
|
|
1116
|
-
variant="ghost"
|
|
1117
|
-
size="sm"
|
|
1118
|
-
className="h-6 gap-1 px-2 text-xs"
|
|
1119
|
-
onClick={handleClear}
|
|
1120
|
-
>
|
|
1121
|
-
<Trash2Icon className="size-3" />
|
|
1122
|
-
Clear
|
|
1123
|
-
</Button>
|
|
1124
|
-
</div>
|
|
1125
|
-
<ComposerPrimitive.Queue>
|
|
1126
|
-
{({ queueItem }) => (
|
|
1127
|
-
<div className="flex items-center gap-2">
|
|
1128
|
-
<ListEndIcon className="size-3.5 shrink-0" />
|
|
1129
|
-
<span className="min-w-0 flex-1 truncate">{queueItem.prompt}</span>
|
|
1130
|
-
{isPiSteerQueueItemId(queueItem.id) && (
|
|
1131
|
-
<span className="border-border rounded-full border px-1.5 text-[10px] uppercase">
|
|
1132
|
-
steer
|
|
1133
|
-
</span>
|
|
1134
|
-
)}
|
|
1135
|
-
</div>
|
|
1136
|
-
)}
|
|
1137
|
-
</ComposerPrimitive.Queue>
|
|
1138
|
-
</div>
|
|
1139
|
-
);
|
|
1140
|
-
};
|
|
1141
|
-
|
|
1142
|
-
const Composer: FC = () => {
|
|
1143
|
-
return (
|
|
1144
|
-
<ComposerPrimitive.Root className="aui-composer-root relative flex w-full flex-col">
|
|
1145
|
-
<ComposerQueue />
|
|
1146
|
-
<ComposerPrimitive.AttachmentDropzone asChild>
|
|
1147
|
-
<div
|
|
1148
|
-
data-slot="aui_composer-shell"
|
|
1149
|
-
className="bg-background focus-within:border-ring/75 focus-within:ring-ring/20 data-[dragging=true]:border-ring data-[dragging=true]:bg-accent/50 flex w-full flex-col gap-2 rounded-(--composer-radius) border p-(--composer-padding) transition-shadow focus-within:ring-2 data-[dragging=true]:border-dashed"
|
|
1150
|
-
>
|
|
1151
|
-
<ComposerAttachments />
|
|
1152
|
-
<ComposerPrimitive.Input
|
|
1153
|
-
placeholder="Send a message..."
|
|
1154
|
-
className="aui-composer-input placeholder:text-muted-foreground/80 max-h-32 min-h-10 w-full resize-none bg-transparent px-1.75 py-1 text-sm outline-none"
|
|
1155
|
-
rows={1}
|
|
1156
|
-
autoFocus
|
|
1157
|
-
aria-label="Message input"
|
|
1158
|
-
/>
|
|
1159
|
-
<ComposerAction />
|
|
1160
|
-
</div>
|
|
1161
|
-
</ComposerPrimitive.AttachmentDropzone>
|
|
1162
|
-
</ComposerPrimitive.Root>
|
|
1163
|
-
);
|
|
1164
|
-
};
|
|
1165
|
-
|
|
1166
|
-
const ComposerAction: FC = () => {
|
|
1167
|
-
return (
|
|
1168
|
-
<div className="aui-composer-action-wrapper relative flex items-center justify-between">
|
|
1169
|
-
<div className="flex items-center gap-1">
|
|
1170
|
-
<ComposerAddAttachment />
|
|
1171
|
-
<ComposerModelSelector />
|
|
1172
|
-
</div>
|
|
1173
|
-
<div className="flex items-center gap-1.5">
|
|
1174
|
-
<ComposerContextRing />
|
|
1175
|
-
{/* Pi queues mid-run sends (follow-up/steer), so Send stays available
|
|
1176
|
-
while text is typed even during a run; Stop shows when empty. */}
|
|
1177
|
-
<AuiIf condition={(s) => !s.thread.isRunning || !s.composer.isEmpty}>
|
|
1178
|
-
<ComposerPrimitive.Send asChild>
|
|
1179
|
-
<TooltipIconButton
|
|
1180
|
-
tooltip="Send message"
|
|
1181
|
-
side="bottom"
|
|
1182
|
-
type="button"
|
|
1183
|
-
variant="default"
|
|
1184
|
-
size="icon"
|
|
1185
|
-
className="aui-composer-send size-8 rounded-full"
|
|
1186
|
-
aria-label="Send message"
|
|
1187
|
-
>
|
|
1188
|
-
<ArrowUpIcon className="aui-composer-send-icon size-4" />
|
|
1189
|
-
</TooltipIconButton>
|
|
1190
|
-
</ComposerPrimitive.Send>
|
|
1191
|
-
</AuiIf>
|
|
1192
|
-
<AuiIf condition={(s) => s.thread.isRunning && s.composer.isEmpty}>
|
|
1193
|
-
<ComposerPrimitive.Cancel asChild>
|
|
1194
|
-
<Button
|
|
1195
|
-
type="button"
|
|
1196
|
-
variant="default"
|
|
1197
|
-
size="icon"
|
|
1198
|
-
className="aui-composer-cancel size-8 rounded-full"
|
|
1199
|
-
aria-label="Stop generating"
|
|
1200
|
-
>
|
|
1201
|
-
<SquareIcon className="aui-composer-cancel-icon size-3 fill-current" />
|
|
1202
|
-
</Button>
|
|
1203
|
-
</ComposerPrimitive.Cancel>
|
|
1204
|
-
</AuiIf>
|
|
1205
|
-
</div>
|
|
1206
|
-
</div>
|
|
1207
|
-
);
|
|
1208
|
-
};
|
|
1209
|
-
|
|
1210
|
-
const selectedModelKey = (provider?: string, modelId?: string) =>
|
|
1211
|
-
provider && modelId ? modelKey(provider, modelId) : undefined;
|
|
1212
|
-
|
|
1213
|
-
/** Model + thinking-level picker seeded from the server handshake and wired to
|
|
1214
|
-
* Pi's per-thread `session.setModel`/`session.setThinkingLevel`. Per-model
|
|
1215
|
-
* thinking levels come from the handshake; Pi clamps unsupported levels and the
|
|
1216
|
-
* effective config is reflected by the next snapshot/event. */
|
|
1217
|
-
const ComposerModelSelector: FC = () => {
|
|
1218
|
-
const handshake = usePiHandshake();
|
|
1219
|
-
const { metadata, setModel, setThinkingLevel, status } = usePiRuntimeExtras();
|
|
1220
|
-
const selected =
|
|
1221
|
-
selectedModelKey(metadata.config?.provider, metadata.config?.modelId) ??
|
|
1222
|
-
handshake?.selectedModelId;
|
|
1223
|
-
const thinkingLevel = metadata.config?.thinkingLevel;
|
|
1224
|
-
if (!handshake || handshake.models.length === 0) return null;
|
|
1225
|
-
|
|
1226
|
-
return (
|
|
1227
|
-
<ModelSelector.Root
|
|
1228
|
-
models={handshake.models}
|
|
1229
|
-
{...(selected ? { value: selected } : {})}
|
|
1230
|
-
onValueChange={(value) => {
|
|
1231
|
-
if (status === "running") return;
|
|
1232
|
-
const model = handshake.models.find((m) => m.id === value);
|
|
1233
|
-
if (!model) return;
|
|
1234
|
-
void setModel({ provider: model.provider, modelId: model.modelId });
|
|
1235
|
-
}}
|
|
1236
|
-
effort={typeof thinkingLevel === "string" ? thinkingLevel : "medium"}
|
|
1237
|
-
onEffortChange={(effort) => {
|
|
1238
|
-
if (status === "running") return;
|
|
1239
|
-
void setThinkingLevel(effort as PiThinkingLevel);
|
|
1240
|
-
}}
|
|
1241
|
-
>
|
|
1242
|
-
<ModelSelector.Trigger variant="ghost" size="sm" />
|
|
1243
|
-
<ModelSelector.Content>
|
|
1244
|
-
<ModelSelector.Search />
|
|
1245
|
-
<ModelSelector.List />
|
|
1246
|
-
{/* Custom slider instead of ModelSelector.Effort: Pi's six thinking
|
|
1247
|
-
levels overflow the horizontal segmented layout. */}
|
|
1248
|
-
<ThinkingLevelSlider />
|
|
1249
|
-
</ModelSelector.Content>
|
|
1250
|
-
</ModelSelector.Root>
|
|
1251
|
-
);
|
|
1252
|
-
};
|
|
1253
|
-
|
|
1254
|
-
/** Context-window usage as a ring beside the send button. Driven by Pi's
|
|
1255
|
-
* `context_usage` events (`usePiThreadState`); hidden until the first run
|
|
1256
|
-
* reports a window. */
|
|
1257
|
-
const ComposerContextRing: FC = () => {
|
|
1258
|
-
const usage = usePiThreadState((s) => s.contextUsage);
|
|
1259
|
-
if (!usage || !usage.contextWindow) return null;
|
|
1260
|
-
return (
|
|
1261
|
-
<ContextDisplay.Ring
|
|
1262
|
-
modelContextWindow={usage.contextWindow}
|
|
1263
|
-
usage={{ totalTokens: usage.tokens ?? 0 }}
|
|
1264
|
-
side="top"
|
|
1265
|
-
/>
|
|
1266
|
-
);
|
|
1267
|
-
};
|
|
1268
|
-
|
|
1269
|
-
const MessageError: FC = () => {
|
|
1270
|
-
return (
|
|
1271
|
-
<MessagePrimitive.Error>
|
|
1272
|
-
<ErrorPrimitive.Root className="aui-message-error-root border-destructive bg-destructive/10 text-destructive dark:bg-destructive/5 mt-2 rounded-md border p-3 text-sm dark:text-red-200">
|
|
1273
|
-
<ErrorPrimitive.Message className="aui-message-error-message line-clamp-2" />
|
|
1274
|
-
</ErrorPrimitive.Root>
|
|
1275
|
-
</MessagePrimitive.Error>
|
|
1276
|
-
);
|
|
1277
|
-
};
|
|
1278
|
-
|
|
1279
|
-
const AssistantMessage: FC = () => {
|
|
1280
|
-
// reserves space for action bar and compensates with `-mb` for consistent msg spacing
|
|
1281
|
-
// keeps hovered action bar from shifting layout (autohide doesn't support absolute positioning well)
|
|
1282
|
-
// for pt-[n] use -mb-[n + 6] & min-h-[n + 6] to preserve compensation
|
|
1283
|
-
const ACTION_BAR_PT = "pt-1.5";
|
|
1284
|
-
const ACTION_BAR_HEIGHT = `-mb-7.5 min-h-7.5 ${ACTION_BAR_PT}`;
|
|
1285
|
-
|
|
1286
|
-
return (
|
|
1287
|
-
<MessagePrimitive.Root
|
|
1288
|
-
data-slot="aui_assistant-message-root"
|
|
1289
|
-
data-role="assistant"
|
|
1290
|
-
className="fade-in slide-in-from-bottom-1 animate-in relative duration-150"
|
|
1291
|
-
>
|
|
1292
|
-
<div
|
|
1293
|
-
data-slot="aui_assistant-message-content"
|
|
1294
|
-
// [contain-intrinsic-size:auto_24px] fixes issue #4104, don't change without checking for regressions
|
|
1295
|
-
className="text-foreground px-2 leading-relaxed wrap-break-word [contain-intrinsic-size:auto_24px] [content-visibility:auto]"
|
|
1296
|
-
>
|
|
1297
|
-
<MessagePrimitive.GroupedParts
|
|
1298
|
-
groupBy={groupPartByType({
|
|
1299
|
-
reasoning: ["group-chainOfThought", "group-reasoning"],
|
|
1300
|
-
"tool-call": ["group-chainOfThought", "group-tool"],
|
|
1301
|
-
"standalone-tool-call": [],
|
|
1302
|
-
})}
|
|
1303
|
-
>
|
|
1304
|
-
{({ part, children }) => {
|
|
1305
|
-
switch (part.type) {
|
|
1306
|
-
case "group-chainOfThought":
|
|
1307
|
-
return <div data-slot="aui_chain-of-thought">{children}</div>;
|
|
1308
|
-
case "group-reasoning": {
|
|
1309
|
-
const running = part.status.type === "running";
|
|
1310
|
-
return (
|
|
1311
|
-
<ReasoningRoot
|
|
1312
|
-
defaultOpen={running}
|
|
1313
|
-
variant="ghost"
|
|
1314
|
-
className="my-1 mb-0"
|
|
1315
|
-
>
|
|
1316
|
-
<ReasoningTrigger active={running} />
|
|
1317
|
-
<ReasoningContent aria-busy={running}>
|
|
1318
|
-
<ReasoningText>{children}</ReasoningText>
|
|
1319
|
-
</ReasoningContent>
|
|
1320
|
-
</ReasoningRoot>
|
|
1321
|
-
);
|
|
1322
|
-
}
|
|
1323
|
-
case "group-tool":
|
|
1324
|
-
if (part.indices.length === 1) return <>{children}</>;
|
|
1325
|
-
return (
|
|
1326
|
-
<ToolGroupRoot variant="ghost" className="my-1">
|
|
1327
|
-
<div className="text-muted-foreground flex items-center gap-2">
|
|
1328
|
-
<WrenchIcon className="size-4 shrink-0" />
|
|
1329
|
-
<ToolGroupTrigger
|
|
1330
|
-
count={part.indices.length}
|
|
1331
|
-
active={part.status.type === "running"}
|
|
1332
|
-
/>
|
|
1333
|
-
</div>
|
|
1334
|
-
<ToolGroupContent className="ml-5">
|
|
1335
|
-
{children}
|
|
1336
|
-
</ToolGroupContent>
|
|
1337
|
-
</ToolGroupRoot>
|
|
1338
|
-
);
|
|
1339
|
-
case "text":
|
|
1340
|
-
return <MarkdownText />;
|
|
1341
|
-
case "reasoning":
|
|
1342
|
-
return <Reasoning {...part} />;
|
|
1343
|
-
case "tool-call":
|
|
1344
|
-
return part.toolUI ?? <ToolFallback {...part} />;
|
|
1345
|
-
case "indicator":
|
|
1346
|
-
return (
|
|
1347
|
-
<span
|
|
1348
|
-
data-slot="aui_assistant-message-indicator"
|
|
1349
|
-
className="animate-pulse font-sans"
|
|
1350
|
-
aria-label="Assistant is working"
|
|
1351
|
-
>
|
|
1352
|
-
{"●"}
|
|
1353
|
-
</span>
|
|
1354
|
-
);
|
|
1355
|
-
default:
|
|
1356
|
-
return null;
|
|
1357
|
-
}
|
|
1358
|
-
}}
|
|
1359
|
-
</MessagePrimitive.GroupedParts>
|
|
1360
|
-
<MessageError />
|
|
1361
|
-
</div>
|
|
1362
|
-
|
|
1363
|
-
<div
|
|
1364
|
-
data-slot="aui_assistant-message-footer"
|
|
1365
|
-
className={cn("ms-2 flex items-center", ACTION_BAR_HEIGHT)}
|
|
1366
|
-
>
|
|
1367
|
-
<BranchPicker />
|
|
1368
|
-
<AssistantActionBar />
|
|
1369
|
-
</div>
|
|
1370
|
-
</MessagePrimitive.Root>
|
|
1371
|
-
);
|
|
1372
|
-
};
|
|
1373
|
-
|
|
1374
|
-
const AssistantActionBar: FC = () => {
|
|
1375
|
-
return (
|
|
1376
|
-
<ActionBarPrimitive.Root
|
|
1377
|
-
hideWhenRunning
|
|
1378
|
-
autohide="not-last"
|
|
1379
|
-
className="aui-assistant-action-bar-root text-muted-foreground col-start-3 row-start-2 -ms-1 flex gap-1"
|
|
1380
|
-
>
|
|
1381
|
-
<ActionBarPrimitive.Copy asChild>
|
|
1382
|
-
<TooltipIconButton tooltip="Copy">
|
|
1383
|
-
<AuiIf condition={(s) => s.message.isCopied}>
|
|
1384
|
-
<CheckIcon />
|
|
1385
|
-
</AuiIf>
|
|
1386
|
-
<AuiIf condition={(s) => !s.message.isCopied}>
|
|
1387
|
-
<CopyIcon />
|
|
1388
|
-
</AuiIf>
|
|
1389
|
-
</TooltipIconButton>
|
|
1390
|
-
</ActionBarPrimitive.Copy>
|
|
1391
|
-
<ActionBarPrimitive.Reload asChild>
|
|
1392
|
-
<TooltipIconButton tooltip="Refresh">
|
|
1393
|
-
<RefreshCwIcon />
|
|
1394
|
-
</TooltipIconButton>
|
|
1395
|
-
</ActionBarPrimitive.Reload>
|
|
1396
|
-
<ActionBarMorePrimitive.Root>
|
|
1397
|
-
<ActionBarMorePrimitive.Trigger asChild>
|
|
1398
|
-
<TooltipIconButton
|
|
1399
|
-
tooltip="More"
|
|
1400
|
-
className="data-[state=open]:bg-accent"
|
|
1401
|
-
>
|
|
1402
|
-
<MoreHorizontalIcon />
|
|
1403
|
-
</TooltipIconButton>
|
|
1404
|
-
</ActionBarMorePrimitive.Trigger>
|
|
1405
|
-
<ActionBarMorePrimitive.Content
|
|
1406
|
-
side="bottom"
|
|
1407
|
-
align="start"
|
|
1408
|
-
className="aui-action-bar-more-content bg-popover text-popover-foreground z-50 min-w-32 overflow-hidden rounded-md border p-1 shadow-md"
|
|
1409
|
-
>
|
|
1410
|
-
<ActionBarPrimitive.ExportMarkdown asChild>
|
|
1411
|
-
<ActionBarMorePrimitive.Item className="aui-action-bar-more-item hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none">
|
|
1412
|
-
<DownloadIcon className="size-4" />
|
|
1413
|
-
Export as Markdown
|
|
1414
|
-
</ActionBarMorePrimitive.Item>
|
|
1415
|
-
</ActionBarPrimitive.ExportMarkdown>
|
|
1416
|
-
</ActionBarMorePrimitive.Content>
|
|
1417
|
-
</ActionBarMorePrimitive.Root>
|
|
1418
|
-
</ActionBarPrimitive.Root>
|
|
1419
|
-
);
|
|
1420
|
-
};
|
|
1421
|
-
|
|
1422
|
-
const UserMessage: FC = () => {
|
|
1423
|
-
return (
|
|
1424
|
-
<MessagePrimitive.Root
|
|
1425
|
-
data-slot="aui_user-message-root"
|
|
1426
|
-
className="fade-in slide-in-from-bottom-1 animate-in grid auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] content-start gap-y-2 px-2 duration-150 [contain-intrinsic-size:auto_60px] [content-visibility:auto] [&:where(>*)]:col-start-2"
|
|
1427
|
-
data-role="user"
|
|
1428
|
-
>
|
|
1429
|
-
<UserMessageAttachments />
|
|
1430
|
-
|
|
1431
|
-
<div className="aui-user-message-content-wrapper relative col-start-2 min-w-0">
|
|
1432
|
-
<div className="aui-user-message-content peer bg-muted text-foreground rounded-2xl px-4 py-2.5 wrap-break-word empty:hidden">
|
|
1433
|
-
<MessagePrimitive.Parts />
|
|
1434
|
-
</div>
|
|
1435
|
-
<div className="aui-user-action-bar-wrapper absolute start-0 top-1/2 -translate-x-full -translate-y-1/2 pe-2 peer-empty:hidden rtl:translate-x-full">
|
|
1436
|
-
<UserActionBar />
|
|
1437
|
-
</div>
|
|
1438
|
-
</div>
|
|
1439
|
-
|
|
1440
|
-
<BranchPicker
|
|
1441
|
-
data-slot="aui_user-branch-picker"
|
|
1442
|
-
className="col-span-full col-start-1 row-start-3 -me-1 justify-end"
|
|
1443
|
-
/>
|
|
1444
|
-
</MessagePrimitive.Root>
|
|
1445
|
-
);
|
|
1446
|
-
};
|
|
1447
|
-
|
|
1448
|
-
const UserActionBar: FC = () => {
|
|
1449
|
-
return (
|
|
1450
|
-
<ActionBarPrimitive.Root
|
|
1451
|
-
hideWhenRunning
|
|
1452
|
-
autohide="not-last"
|
|
1453
|
-
className="aui-user-action-bar-root flex flex-col items-end"
|
|
1454
|
-
>
|
|
1455
|
-
<ActionBarPrimitive.Edit asChild>
|
|
1456
|
-
<TooltipIconButton tooltip="Edit" className="aui-user-action-edit p-4">
|
|
1457
|
-
<PencilIcon />
|
|
1458
|
-
</TooltipIconButton>
|
|
1459
|
-
</ActionBarPrimitive.Edit>
|
|
1460
|
-
</ActionBarPrimitive.Root>
|
|
1461
|
-
);
|
|
1462
|
-
};
|
|
1463
|
-
|
|
1464
|
-
const EditComposer: FC = () => {
|
|
1465
|
-
return (
|
|
1466
|
-
<MessagePrimitive.Root
|
|
1467
|
-
data-slot="aui_edit-composer-wrapper"
|
|
1468
|
-
className="flex flex-col px-2"
|
|
1469
|
-
>
|
|
1470
|
-
<ComposerPrimitive.Root className="aui-edit-composer-root bg-muted ms-auto flex w-full max-w-[85%] flex-col rounded-2xl">
|
|
1471
|
-
<ComposerPrimitive.Input
|
|
1472
|
-
className="aui-edit-composer-input text-foreground min-h-14 w-full resize-none bg-transparent p-4 text-sm outline-none"
|
|
1473
|
-
autoFocus
|
|
1474
|
-
/>
|
|
1475
|
-
<div className="aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end">
|
|
1476
|
-
<ComposerPrimitive.Cancel asChild>
|
|
1477
|
-
<Button variant="ghost" size="sm">
|
|
1478
|
-
Cancel
|
|
1479
|
-
</Button>
|
|
1480
|
-
</ComposerPrimitive.Cancel>
|
|
1481
|
-
<ComposerPrimitive.Send asChild>
|
|
1482
|
-
<Button size="sm">Update</Button>
|
|
1483
|
-
</ComposerPrimitive.Send>
|
|
1484
|
-
</div>
|
|
1485
|
-
</ComposerPrimitive.Root>
|
|
1486
|
-
</MessagePrimitive.Root>
|
|
1487
|
-
);
|
|
1488
|
-
};
|
|
1489
|
-
|
|
1490
|
-
const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
|
|
1491
|
-
className,
|
|
1492
|
-
...rest
|
|
1493
|
-
}) => {
|
|
1494
|
-
return (
|
|
1495
|
-
<BranchPickerPrimitive.Root
|
|
1496
|
-
hideWhenSingleBranch
|
|
1497
|
-
className={cn(
|
|
1498
|
-
"aui-branch-picker-root text-muted-foreground -ms-2 me-2 inline-flex items-center text-xs",
|
|
1499
|
-
className,
|
|
1500
|
-
)}
|
|
1501
|
-
{...rest}
|
|
1502
|
-
>
|
|
1503
|
-
<BranchPickerPrimitive.Previous asChild>
|
|
1504
|
-
<TooltipIconButton tooltip="Previous">
|
|
1505
|
-
<ChevronLeftIcon />
|
|
1506
|
-
</TooltipIconButton>
|
|
1507
|
-
</BranchPickerPrimitive.Previous>
|
|
1508
|
-
<span className="aui-branch-picker-state font-medium">
|
|
1509
|
-
<BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
|
|
1510
|
-
</span>
|
|
1511
|
-
<BranchPickerPrimitive.Next asChild>
|
|
1512
|
-
<TooltipIconButton tooltip="Next">
|
|
1513
|
-
<ChevronRightIcon />
|
|
1514
|
-
</TooltipIconButton>
|
|
1515
|
-
</BranchPickerPrimitive.Next>
|
|
1516
|
-
</BranchPickerPrimitive.Root>
|
|
1517
|
-
);
|
|
1518
|
-
};
|
|
1519
|
-
|
|
1520
|
-
```
|
|
1521
|
-
|
|
1522
|
-
## components/pi-handshake.tsx
|
|
1523
|
-
|
|
1524
|
-
```tsx
|
|
1525
|
-
"use client";
|
|
1526
|
-
|
|
1527
|
-
import { createContext, useContext, type ReactNode } from "react";
|
|
1528
|
-
import type { ModelOption } from "@/components/assistant-ui/model-selector";
|
|
1529
|
-
|
|
1530
|
-
/** The slice of the server handshake the composer needs to render its model
|
|
1531
|
-
* selector. Provided by the page (which fetches `/api/pi/handshake`) and
|
|
1532
|
-
* consumed deep inside the composer, so the model picker can live in the
|
|
1533
|
-
* composer action bar without prop-drilling through the Thread. */
|
|
1534
|
-
export type PiModelOption = ModelOption & {
|
|
1535
|
-
provider: string;
|
|
1536
|
-
modelId: string;
|
|
1537
|
-
};
|
|
1538
|
-
|
|
1539
|
-
export type PiHandshakeValue = {
|
|
1540
|
-
models: PiModelOption[];
|
|
1541
|
-
selectedModelId?: string | undefined;
|
|
1542
|
-
};
|
|
1543
|
-
|
|
1544
|
-
const PiHandshakeContext = createContext<PiHandshakeValue | null>(null);
|
|
1545
|
-
|
|
1546
|
-
export function PiHandshakeProvider({
|
|
1547
|
-
value,
|
|
1548
|
-
children,
|
|
1549
|
-
}: {
|
|
1550
|
-
value: PiHandshakeValue | null;
|
|
1551
|
-
children: ReactNode;
|
|
1552
|
-
}) {
|
|
1553
|
-
return (
|
|
1554
|
-
<PiHandshakeContext.Provider value={value}>
|
|
1555
|
-
{children}
|
|
1556
|
-
</PiHandshakeContext.Provider>
|
|
1557
|
-
);
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
export function usePiHandshake(): PiHandshakeValue | null {
|
|
1561
|
-
return useContext(PiHandshakeContext);
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
```
|
|
1565
|
-
|
|
1566
|
-
## components/workspace-browser.tsx
|
|
1567
|
-
|
|
1568
|
-
```tsx
|
|
1569
|
-
"use client";
|
|
1570
|
-
|
|
1571
|
-
import { useRef, useState } from "react";
|
|
1572
|
-
import { ArrowLeft, Folder } from "lucide-react";
|
|
1573
|
-
import type { FsListing } from "@/app/api/pi/fs/route";
|
|
1574
|
-
import {
|
|
1575
|
-
Dialog,
|
|
1576
|
-
DialogClose,
|
|
1577
|
-
DialogContent,
|
|
1578
|
-
DialogDescription,
|
|
1579
|
-
DialogFooter,
|
|
1580
|
-
DialogHeader,
|
|
1581
|
-
DialogTitle,
|
|
1582
|
-
DialogTrigger,
|
|
1583
|
-
} from "@/components/ui/radix/dialog";
|
|
1584
|
-
import { Button } from "@/components/ui/button";
|
|
1585
|
-
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
1586
|
-
import { Spinner } from "@/components/ui/spinner";
|
|
1587
|
-
|
|
1588
|
-
/** Server-backed directory browser. Click a folder to navigate in; the footer
|
|
1589
|
-
* commits whatever directory is currently in view. */
|
|
1590
|
-
export function WorkspaceBrowser({
|
|
1591
|
-
value,
|
|
1592
|
-
onCommit,
|
|
1593
|
-
}: {
|
|
1594
|
-
value: string;
|
|
1595
|
-
onCommit: (path: string) => void;
|
|
1596
|
-
}) {
|
|
1597
|
-
const [open, setOpen] = useState(false);
|
|
1598
|
-
const [listing, setListing] = useState<FsListing | null>(null);
|
|
1599
|
-
const [loading, setLoading] = useState(false);
|
|
1600
|
-
const [error, setError] = useState<string | null>(null);
|
|
1601
|
-
// Monotonic request token: rapid navigation fires overlapping fetches, and a
|
|
1602
|
-
// slow earlier response must not overwrite the directory navigated to last.
|
|
1603
|
-
const requestSeq = useRef(0);
|
|
1604
|
-
|
|
1605
|
-
const load = (path?: string) => {
|
|
1606
|
-
const seq = ++requestSeq.current;
|
|
1607
|
-
setLoading(true);
|
|
1608
|
-
setError(null);
|
|
1609
|
-
const qs = path ? `?path=${encodeURIComponent(path)}` : "";
|
|
1610
|
-
fetch(`/api/pi/fs${qs}`)
|
|
1611
|
-
.then(async (response) => {
|
|
1612
|
-
if (!response.ok) {
|
|
1613
|
-
throw new Error(`Failed to list directory (HTTP ${response.status})`);
|
|
1614
|
-
}
|
|
1615
|
-
return (await response.json()) as FsListing;
|
|
1616
|
-
})
|
|
1617
|
-
.then((data) => {
|
|
1618
|
-
if (seq === requestSeq.current) setListing(data);
|
|
1619
|
-
})
|
|
1620
|
-
.catch((err: unknown) => {
|
|
1621
|
-
if (seq === requestSeq.current) {
|
|
1622
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
1623
|
-
}
|
|
1624
|
-
})
|
|
1625
|
-
.finally(() => {
|
|
1626
|
-
if (seq === requestSeq.current) setLoading(false);
|
|
1627
|
-
});
|
|
1628
|
-
};
|
|
1629
|
-
|
|
1630
|
-
const currentName = value.split("/").filter(Boolean).at(-1);
|
|
1631
|
-
|
|
1632
|
-
return (
|
|
1633
|
-
<Dialog
|
|
1634
|
-
open={open}
|
|
1635
|
-
onOpenChange={(next) => {
|
|
1636
|
-
setOpen(next);
|
|
1637
|
-
if (next) load(value || undefined);
|
|
1638
|
-
}}
|
|
1639
|
-
>
|
|
1640
|
-
<div className="flex items-center gap-2">
|
|
1641
|
-
{currentName && (
|
|
1642
|
-
<span
|
|
1643
|
-
className="text-muted-foreground max-w-72 truncate font-mono text-xs"
|
|
1644
|
-
title={value}
|
|
1645
|
-
>
|
|
1646
|
-
…/{currentName}
|
|
1647
|
-
</span>
|
|
1648
|
-
)}
|
|
1649
|
-
<DialogTrigger asChild>
|
|
1650
|
-
<Button variant="outline" size="sm">
|
|
1651
|
-
Browse…
|
|
1652
|
-
</Button>
|
|
1653
|
-
</DialogTrigger>
|
|
1654
|
-
</div>
|
|
1655
|
-
|
|
1656
|
-
<DialogContent className="sm:max-w-lg">
|
|
1657
|
-
<DialogHeader className="min-w-0">
|
|
1658
|
-
<DialogTitle>Select workspace</DialogTitle>
|
|
1659
|
-
<DialogDescription className="truncate font-mono text-xs">
|
|
1660
|
-
{listing?.path ?? "…"}
|
|
1661
|
-
</DialogDescription>
|
|
1662
|
-
</DialogHeader>
|
|
1663
|
-
|
|
1664
|
-
<div className="flex items-center gap-2">
|
|
1665
|
-
<Button
|
|
1666
|
-
variant="ghost"
|
|
1667
|
-
size="xs"
|
|
1668
|
-
disabled={!listing?.parent || loading}
|
|
1669
|
-
onClick={() => listing?.parent && load(listing.parent)}
|
|
1670
|
-
>
|
|
1671
|
-
<ArrowLeft /> Back
|
|
1672
|
-
</Button>
|
|
1673
|
-
{loading && <Spinner className="size-4" />}
|
|
1674
|
-
</div>
|
|
1675
|
-
|
|
1676
|
-
<ScrollArea className="h-72 min-w-0 rounded-md border [&_[data-radix-scroll-area-viewport]>div]:!block">
|
|
1677
|
-
{error && (
|
|
1678
|
-
<p className="text-destructive px-3 py-2 text-xs">{error}</p>
|
|
1679
|
-
)}
|
|
1680
|
-
{listing?.entries.map((entry) => (
|
|
1681
|
-
<button
|
|
1682
|
-
key={entry.path}
|
|
1683
|
-
type="button"
|
|
1684
|
-
onClick={() => load(entry.path)}
|
|
1685
|
-
className="hover:bg-accent flex w-full min-w-0 items-center gap-2 px-3 py-1.5 text-left font-mono text-xs"
|
|
1686
|
-
>
|
|
1687
|
-
<Folder className="size-3.5 shrink-0" />
|
|
1688
|
-
<span className="truncate">{entry.name}</span>
|
|
1689
|
-
</button>
|
|
1690
|
-
))}
|
|
1691
|
-
{listing && listing.entries.length === 0 && (
|
|
1692
|
-
<p className="text-muted-foreground px-3 py-2 text-xs">
|
|
1693
|
-
No subfolders
|
|
1694
|
-
</p>
|
|
1695
|
-
)}
|
|
1696
|
-
</ScrollArea>
|
|
1697
|
-
|
|
1698
|
-
<DialogFooter>
|
|
1699
|
-
<DialogClose asChild>
|
|
1700
|
-
<Button variant="ghost" size="sm">
|
|
1701
|
-
Cancel
|
|
1702
|
-
</Button>
|
|
1703
|
-
</DialogClose>
|
|
1704
|
-
<Button
|
|
1705
|
-
size="sm"
|
|
1706
|
-
disabled={!listing}
|
|
1707
|
-
onClick={() => {
|
|
1708
|
-
if (listing) onCommit(listing.path);
|
|
1709
|
-
setOpen(false);
|
|
1710
|
-
}}
|
|
1711
|
-
>
|
|
1712
|
-
Select this folder
|
|
1713
|
-
</Button>
|
|
1714
|
-
</DialogFooter>
|
|
1715
|
-
</DialogContent>
|
|
1716
|
-
</Dialog>
|
|
1717
|
-
);
|
|
1718
|
-
}
|
|
1719
|
-
|
|
1720
|
-
```
|
|
1721
|
-
|
|
1722
|
-
## lib/http.ts
|
|
1723
|
-
|
|
1724
|
-
```typescript
|
|
1725
|
-
/** Shared response helpers for the Pi example routes. */
|
|
1726
|
-
|
|
1727
|
-
/** 500 with a JSON `{ error }` body — `createPiHttpClient` surfaces the text. */
|
|
1728
|
-
export const fail = (error: unknown): Response =>
|
|
1729
|
-
Response.json(
|
|
1730
|
-
{ error: error instanceof Error ? error.message : String(error) },
|
|
1731
|
-
{ status: 500 },
|
|
1732
|
-
);
|
|
1733
|
-
|
|
1734
|
-
/** 400 with a JSON `{ error }` body, for invalid request input. */
|
|
1735
|
-
export const badRequest = (message: string): Response =>
|
|
1736
|
-
Response.json({ error: message }, { status: 400 });
|
|
1737
|
-
|
|
1738
|
-
/** 204 for write endpoints that return nothing. */
|
|
1739
|
-
export const noContent = (): Response => new Response(null, { status: 204 });
|
|
1740
|
-
|
|
1741
|
-
/** Wrap a route handler so any thrown error becomes a `fail()` response. */
|
|
1742
|
-
export const withFail =
|
|
1743
|
-
<Args extends unknown[]>(
|
|
1744
|
-
handler: (...args: Args) => Response | Promise<Response>,
|
|
1745
|
-
) =>
|
|
1746
|
-
async (...args: Args): Promise<Response> => {
|
|
1747
|
-
try {
|
|
1748
|
-
return await handler(...args);
|
|
1749
|
-
} catch (error) {
|
|
1750
|
-
return fail(error);
|
|
1751
|
-
}
|
|
1752
|
-
};
|
|
1753
|
-
|
|
1754
|
-
```
|
|
1755
|
-
|
|
1756
|
-
## lib/model-key.ts
|
|
1757
|
-
|
|
1758
|
-
```typescript
|
|
1759
|
-
/** Stable, collision-free model option id — model ids repeat across providers.
|
|
1760
|
-
* Shared by the server handshake and the composer's model selector; the two
|
|
1761
|
-
* must agree for the seeded selection to preselect in the UI. */
|
|
1762
|
-
export const modelKey = (provider: string, modelId: string): string =>
|
|
1763
|
-
`${provider}:${modelId}`;
|
|
1764
|
-
|
|
1765
|
-
```
|
|
1766
|
-
|
|
1767
|
-
## lib/pi-server.ts
|
|
1768
|
-
|
|
1769
|
-
```typescript
|
|
1770
|
-
/**
|
|
1771
|
-
* Server-only Pi wiring for the example.
|
|
1772
|
-
*
|
|
1773
|
-
* Owns the process-singleton `createPiNodeClient` (the in-process SDK host) and
|
|
1774
|
-
* the env-seeded model resolution. Imported only from route
|
|
1775
|
-
* handlers — never from a client component — so the Pi SDK and its file I/O stay
|
|
1776
|
-
* on the server.
|
|
1777
|
-
*
|
|
1778
|
-
* Model resolution mirrors what Pi's own `createAgentSession` does: an explicit
|
|
1779
|
-
* PI_PROVIDER + PI_MODEL_ID wins, otherwise we fall back to Pi's configured
|
|
1780
|
-
* default (`settings.json`'s `defaultProvider`/`defaultModel`, read via
|
|
1781
|
-
* `SettingsManager`). The resolved `Model` is handed to the supervisor as the
|
|
1782
|
-
* default for every new session, and powers the model-selector catalog and the
|
|
1783
|
-
* readiness banner. So a user who is simply authenticated with `pi` (and has a
|
|
1784
|
-
* default model picked) needs no env at all.
|
|
1785
|
-
*
|
|
1786
|
-
* `PI_CODING_AGENT_DIR` is Pi's own env var for the agent config dir
|
|
1787
|
-
* (~/.pi/agent by default); when set, every Pi config source below points at it.
|
|
1788
|
-
*/
|
|
1789
|
-
import {
|
|
1790
|
-
ModelRegistry,
|
|
1791
|
-
ModelRuntime,
|
|
1792
|
-
SettingsManager,
|
|
1793
|
-
} from "@earendil-works/pi-coding-agent";
|
|
1794
|
-
import {
|
|
1795
|
-
createPiNodeClient,
|
|
1796
|
-
type PiModelInfo,
|
|
1797
|
-
type PiRuntimeReadiness,
|
|
1798
|
-
type PiThinkingLevel,
|
|
1799
|
-
} from "@assistant-ui/react-pi/node";
|
|
1800
|
-
import { modelKey } from "./model-key";
|
|
1801
|
-
|
|
1802
|
-
const env = (key: string): string | undefined => {
|
|
1803
|
-
const value = process.env[key];
|
|
1804
|
-
return value && value.trim() ? value.trim() : undefined;
|
|
1805
|
-
};
|
|
1806
|
-
|
|
1807
|
-
const workspacePath = env("PI_WORKSPACE_PATH") ?? process.cwd();
|
|
1808
|
-
const agentDir = env("PI_CODING_AGENT_DIR");
|
|
1809
|
-
|
|
1810
|
-
const modelRuntime = await ModelRuntime.create(
|
|
1811
|
-
agentDir ? { authPath: `${agentDir}/auth.json` } : {},
|
|
1812
|
-
);
|
|
1813
|
-
const modelRegistry = new ModelRegistry(modelRuntime);
|
|
1814
|
-
const settingsManager = SettingsManager.create(workspacePath, agentDir);
|
|
1815
|
-
|
|
1816
|
-
// Env wins; otherwise fall back to Pi's configured default (settings.json).
|
|
1817
|
-
const provider = env("PI_PROVIDER") ?? settingsManager.getDefaultProvider();
|
|
1818
|
-
const modelId = env("PI_MODEL_ID") ?? settingsManager.getDefaultModel();
|
|
1819
|
-
const modelSource: "env" | "pi-default" =
|
|
1820
|
-
env("PI_PROVIDER") && env("PI_MODEL_ID") ? "env" : "pi-default";
|
|
1821
|
-
|
|
1822
|
-
const seededModel =
|
|
1823
|
-
provider && modelId ? modelRegistry.find(provider, modelId) : undefined;
|
|
1824
|
-
|
|
1825
|
-
export const piClient = createPiNodeClient({
|
|
1826
|
-
workspacePath,
|
|
1827
|
-
...(agentDir ? { agentDir } : {}),
|
|
1828
|
-
...(seededModel ? { model: seededModel } : {}),
|
|
1829
|
-
});
|
|
1830
|
-
|
|
1831
|
-
export type PiModelOption = {
|
|
1832
|
-
id: string;
|
|
1833
|
-
name: string;
|
|
1834
|
-
description?: string;
|
|
1835
|
-
provider: string;
|
|
1836
|
-
modelId: string;
|
|
1837
|
-
/** Thinking levels the model supports; omitted for non-reasoning models. */
|
|
1838
|
-
efforts?: { id: string; name: string }[];
|
|
1839
|
-
};
|
|
1840
|
-
|
|
1841
|
-
/** Display names for Pi's thinking levels (the UI-facing half of `efforts`;
|
|
1842
|
-
* which levels a model supports comes from `PiModelInfo`). */
|
|
1843
|
-
const THINKING_LEVEL_NAMES: Record<PiThinkingLevel, string> = {
|
|
1844
|
-
off: "Off",
|
|
1845
|
-
minimal: "Minimal",
|
|
1846
|
-
low: "Low",
|
|
1847
|
-
medium: "Medium",
|
|
1848
|
-
high: "High",
|
|
1849
|
-
xhigh: "xHigh",
|
|
1850
|
-
};
|
|
1851
|
-
|
|
1852
|
-
const ALL_THINKING_LEVELS = Object.keys(
|
|
1853
|
-
THINKING_LEVEL_NAMES,
|
|
1854
|
-
) as PiThinkingLevel[];
|
|
1855
|
-
|
|
1856
|
-
const thinkingEfforts = (
|
|
1857
|
-
model: PiModelInfo,
|
|
1858
|
-
): { id: string; name: string }[] | undefined => {
|
|
1859
|
-
if (!model.supportsThinking) return undefined;
|
|
1860
|
-
// No `availableThinkingLevels` means the model declares no per-level map;
|
|
1861
|
-
// every level stays selectable (provider defaults apply).
|
|
1862
|
-
return (model.availableThinkingLevels ?? ALL_THINKING_LEVELS).map((id) => ({
|
|
1863
|
-
id,
|
|
1864
|
-
name: THINKING_LEVEL_NAMES[id],
|
|
1865
|
-
}));
|
|
1866
|
-
};
|
|
1867
|
-
|
|
1868
|
-
export type PiHandshake = {
|
|
1869
|
-
workspacePath: string;
|
|
1870
|
-
/** Models Pi can run here (auth-configured first, else the full catalog). */
|
|
1871
|
-
models: PiModelOption[];
|
|
1872
|
-
/** The env-seeded selection, shown as the selector's default. */
|
|
1873
|
-
selectedModelId: string | undefined;
|
|
1874
|
-
readiness: PiRuntimeReadiness;
|
|
1875
|
-
};
|
|
1876
|
-
|
|
1877
|
-
const computeReadiness = (): PiRuntimeReadiness => {
|
|
1878
|
-
if (!provider || !modelId) {
|
|
1879
|
-
return {
|
|
1880
|
-
state: "missing-model",
|
|
1881
|
-
message:
|
|
1882
|
-
"No default model configured. Pick one with `pi`, or set PI_PROVIDER and PI_MODEL_ID.",
|
|
1883
|
-
};
|
|
1884
|
-
}
|
|
1885
|
-
if (!seededModel) {
|
|
1886
|
-
return {
|
|
1887
|
-
state: "unavailable-model",
|
|
1888
|
-
selection: { provider, modelId },
|
|
1889
|
-
message: `${provider}/${modelId} is not in Pi's model registry.`,
|
|
1890
|
-
};
|
|
1891
|
-
}
|
|
1892
|
-
if (!modelRegistry.hasConfiguredAuth(seededModel)) {
|
|
1893
|
-
return {
|
|
1894
|
-
state: "missing-credentials",
|
|
1895
|
-
provider,
|
|
1896
|
-
message: `No credentials configured for ${provider}. Authenticate with \`pi\`, then restart the server.`,
|
|
1897
|
-
};
|
|
1898
|
-
}
|
|
1899
|
-
return {
|
|
1900
|
-
state: "ready",
|
|
1901
|
-
selection: { provider, modelId },
|
|
1902
|
-
source: modelSource,
|
|
1903
|
-
};
|
|
1904
|
-
};
|
|
1905
|
-
|
|
1906
|
-
export const getHandshake = async (): Promise<PiHandshake> => {
|
|
1907
|
-
const catalog = await piClient.getAvailableModels();
|
|
1908
|
-
return {
|
|
1909
|
-
workspacePath,
|
|
1910
|
-
models: catalog.map((model) => {
|
|
1911
|
-
const efforts = thinkingEfforts(model);
|
|
1912
|
-
return {
|
|
1913
|
-
id: modelKey(model.provider, model.modelId),
|
|
1914
|
-
name: model.name ?? model.modelId,
|
|
1915
|
-
description: model.provider,
|
|
1916
|
-
provider: model.provider,
|
|
1917
|
-
modelId: model.modelId,
|
|
1918
|
-
...(efforts ? { efforts } : {}),
|
|
1919
|
-
};
|
|
1920
|
-
}),
|
|
1921
|
-
selectedModelId: seededModel
|
|
1922
|
-
? modelKey(String(seededModel.provider), seededModel.id)
|
|
1923
|
-
: undefined,
|
|
1924
|
-
readiness: computeReadiness(),
|
|
1925
|
-
};
|
|
1926
|
-
};
|
|
1927
|
-
|
|
1928
|
-
```
|
|
1929
|
-
|
|
1930
|
-
## next.config.ts
|
|
1931
|
-
|
|
1932
|
-
```typescript
|
|
1933
|
-
import { withAui } from "@assistant-ui/next";
|
|
1934
|
-
import type { NextConfig } from "next";
|
|
1935
|
-
|
|
1936
|
-
const nextConfig: NextConfig = {
|
|
1937
|
-
// The Pi SDK is a native Node dependency; keep it external to the server
|
|
1938
|
-
// bundle so Next doesn't try to trace/bundle it.
|
|
1939
|
-
serverExternalPackages: ["@earendil-works/pi-coding-agent"],
|
|
1940
|
-
};
|
|
1941
|
-
|
|
1942
|
-
export default withAui(nextConfig);
|
|
1943
|
-
|
|
1944
|
-
```
|
|
1945
|
-
|
|
1946
|
-
## package.json
|
|
1947
|
-
|
|
1948
|
-
```json
|
|
1949
|
-
{
|
|
1950
|
-
"name": "with-pi",
|
|
1951
|
-
"version": "0.0.0",
|
|
1952
|
-
"private": true,
|
|
1953
|
-
"type": "module",
|
|
1954
|
-
"scripts": {
|
|
1955
|
-
"dev": "next dev",
|
|
1956
|
-
"build": "next build",
|
|
1957
|
-
"start": "next start"
|
|
1958
|
-
},
|
|
1959
|
-
"dependencies": {
|
|
1960
|
-
"@assistant-ui/react": "workspace:*",
|
|
1961
|
-
"@assistant-ui/react-markdown": "workspace:*",
|
|
1962
|
-
"@assistant-ui/react-pi": "workspace:*",
|
|
1963
|
-
"@assistant-ui/ui": "workspace:*",
|
|
1964
|
-
"@earendil-works/pi-coding-agent": "^0.84.2",
|
|
1965
|
-
"class-variance-authority": "^0.7.1",
|
|
1966
|
-
"clsx": "^2.1.1",
|
|
1967
|
-
"lucide-react": "^1.33.0",
|
|
1968
|
-
"next": "^16.3.2",
|
|
1969
|
-
"radix-ui": "^1.6.7",
|
|
1970
|
-
"react": "^19.2.8",
|
|
1971
|
-
"react-dom": "^19.2.8",
|
|
1972
|
-
"tailwind-merge": "^3.6.0"
|
|
1973
|
-
},
|
|
1974
|
-
"devDependencies": {
|
|
1975
|
-
"@assistant-ui/next": "workspace:*",
|
|
1976
|
-
"@assistant-ui/x-buildutils": "workspace:*",
|
|
1977
|
-
"@tailwindcss/postcss": "^4.3.3",
|
|
1978
|
-
"@types/node": "^26.2.0",
|
|
1979
|
-
"@types/react": "^19.2.18",
|
|
1980
|
-
"@types/react-dom": "^19.2.5",
|
|
1981
|
-
"postcss": "^8.5.26",
|
|
1982
|
-
"tailwindcss": "^4.3.3",
|
|
1983
|
-
"tw-animate-css": "^1.4.0",
|
|
1984
|
-
"typescript": "^7.0.2"
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
|
-
|
|
1988
|
-
```
|
|
1989
|
-
|
|
1990
|
-
## README.md
|
|
1991
|
-
|
|
1992
|
-
```markdown
|
|
1993
|
-
# assistant-ui × Pi
|
|
1994
|
-
|
|
1995
|
-
A minimal local harness for the [`@assistant-ui/react-pi`](../../packages/react-pi)
|
|
1996
|
-
adapter, driving the [Pi coding agent](https://www.npmjs.com/package/@earendil-works/pi-coding-agent)
|
|
1997
|
-
in-process.
|
|
1998
|
-
|
|
1999
|
-
It demonstrates the MVP surface: a thread list, new sessions, a composer with
|
|
2000
|
-
steer / follow-up / stop, streaming assistant responses (text + reasoning), tool
|
|
2001
|
-
cards with streaming output, the blocking host-UI dialogs (confirm / input /
|
|
2002
|
-
select / editor), a model + credential readiness banner, a context-usage
|
|
2003
|
-
indicator, and a read-only workspace browser (`components/workspace-browser.tsx`
|
|
2004
|
-
backed by `/api/pi/fs`) for inspecting the agent's working directory. It proves
|
|
2005
|
-
the adapter surface, not a full desktop app shell.
|
|
2006
|
-
|
|
2007
|
-
## How it fits together
|
|
2008
|
-
|
|
2009
|
-
```
|
|
2010
|
-
browser server (Next.js, Node runtime)
|
|
2011
|
-
───────── ──────────────────────────────
|
|
2012
|
-
usePiRuntime app/api/pi/** ← route layer
|
|
2013
|
-
└ createPiHttpClient ──HTTP──▶ piClient = createPiNodeClient(...)
|
|
2014
|
-
(fetch + SSE) └ PiThreadSupervisor → Pi SDK (AgentSession)
|
|
2015
|
-
```
|
|
2016
|
-
|
|
2017
|
-
`createPiHttpClient` and the route layer are two halves of one `PiClient`
|
|
2018
|
-
contract: the browser talks HTTP/SSE, the server runs the SDK in-process behind
|
|
2019
|
-
the process-singleton supervisor.
|
|
2020
|
-
|
|
2021
|
-
## Model resolution
|
|
2022
|
-
|
|
2023
|
-
Resolution mirrors Pi's own `createAgentSession`: if you set
|
|
2024
|
-
`PI_PROVIDER` + `PI_MODEL_ID` they win; otherwise the server falls back to Pi's
|
|
2025
|
-
configured default (`settings.json`'s `defaultProvider`/`defaultModel`). So if
|
|
2026
|
-
you're authenticated with `pi` and have a default model picked, **no env is
|
|
2027
|
-
required**. The resolved model is handed to every new session and seeds the
|
|
2028
|
-
in-app **model selector** (via `@assistant-ui/ui`'s `ModelSelector`) and the
|
|
2029
|
-
readiness banner's credential check. The selector is wired to Pi's per-thread
|
|
2030
|
-
`setModel`, and the composer exposes Pi's thinking-level control. Set
|
|
2031
|
-
`PI_CODING_AGENT_DIR` to point at a non-default agent config dir (defaults to
|
|
2032
|
-
`~/.pi/agent`).
|
|
2033
|
-
|
|
2034
|
-
## Run it
|
|
2035
|
-
|
|
2036
|
-
1. Authenticate Pi once and pick a default model (writes `~/.pi/agent/`):
|
|
2037
|
-
|
|
2038
|
-
```bash
|
|
2039
|
-
pnpm dlx @earendil-works/pi-coding-agent
|
|
2040
|
-
```
|
|
2041
|
-
|
|
2042
|
-
2. Start it (no env needed if Pi has a default model):
|
|
2043
|
-
|
|
2044
|
-
```bash
|
|
2045
|
-
pnpm dev
|
|
2046
|
-
```
|
|
2047
|
-
|
|
2048
|
-
To override the model or workspace, copy `.env.example` to `.env.local` and
|
|
2049
|
-
set `PI_PROVIDER` / `PI_MODEL_ID` / `PI_WORKSPACE_PATH`.
|
|
2050
|
-
|
|
2051
|
-
The agent reads and writes files and runs shell commands in `PI_WORKSPACE_PATH`.
|
|
2052
|
-
Point it at a scratch directory.
|
|
2053
|
-
|
|
2054
|
-
```
|
|
2055
|
-
|
|
2056
|
-
## tsconfig.json
|
|
2057
|
-
|
|
2058
|
-
```json
|
|
2059
|
-
{
|
|
2060
|
-
"extends": "@assistant-ui/x-buildutils/ts/next",
|
|
2061
|
-
"compilerOptions": {
|
|
2062
|
-
"paths": {
|
|
2063
|
-
"@/*": ["./*"],
|
|
2064
|
-
"@/components/assistant-ui/*": [
|
|
2065
|
-
"../../packages/ui/src/components/assistant-ui/*"
|
|
2066
|
-
],
|
|
2067
|
-
"@/hooks/*": ["../../packages/ui/src/hooks/*"],
|
|
2068
|
-
"@/components/ui/*": [
|
|
2069
|
-
"../../packages/ui/src/components/ui/base/*",
|
|
2070
|
-
"../../packages/ui/src/components/ui/radix/*"
|
|
2071
|
-
],
|
|
2072
|
-
"@/components/ui/radix/*": [
|
|
2073
|
-
"../../packages/ui/src/components/ui/radix/*"
|
|
2074
|
-
],
|
|
2075
|
-
"@/lib/utils": ["../../packages/ui/src/lib/utils"],
|
|
2076
|
-
"@assistant-ui/ui/*": ["../../packages/ui/src/*"]
|
|
2077
|
-
}
|
|
2078
|
-
},
|
|
2079
|
-
"include": ["**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
2080
|
-
"exclude": ["node_modules"]
|
|
2081
|
-
}
|
|
2082
|
-
|
|
2083
|
-
```
|
|
2084
|
-
|