| author | |
| committer | |
| log | db4bf9497b524f35e665af9e941aa8faeb05ea9e |
| tree | 7ea41f309200216d073db044e37e6b1826f6bcb2 |
| parent | b8262b41228e03b2e1e14b701be449034349c8ba |
| signature | Signed by PGP key B5690EEEBB952194 |
41 files changed, 17 insertions(+), 744 deletions(-)
.github/workflows/release.yml-29| ... | ... | @@ -851,35 +851,6 @@ jobs: |
| 851 | 851 | fi |
| 852 | 852 | done |
| 853 | 853 | |
| 854 | # - name: Merge Windows updater manifests | |
| 855 | # run: | | |
| 856 | # shopt -s nullglob | |
| 857 | # found_windows_manifest=false | |
| 858 | # for x64_manifest in release-assets/*-win-x64.yml; do | |
| 859 | # if [[ "$(basename "$x64_manifest")" == builder-debug-* ]]; then | |
| 860 | # continue | |
| 861 | # fi | |
| 862 | ||
| 863 | # arm64_manifest="${x64_manifest/-x64.yml/-arm64.yml}" | |
| 864 | # output_manifest="${x64_manifest/-win-x64.yml/.yml}" | |
| 865 | # if [[ ! -f "$arm64_manifest" ]]; then | |
| 866 | # echo "Missing matching arm64 Windows manifest for $x64_manifest" >&2 | |
| 867 | # exit 1 | |
| 868 | # fi | |
| 869 | ||
| 870 | # found_windows_manifest=true | |
| 871 | # node scripts/merge-update-manifests.ts --platform win \ | |
| 872 | # "$arm64_manifest" \ | |
| 873 | # "$x64_manifest" \ | |
| 874 | # "$output_manifest" | |
| 875 | # rm -f "$arm64_manifest" "$x64_manifest" | |
| 876 | # done | |
| 877 | ||
| 878 | # if [[ "$found_windows_manifest" != true ]]; then | |
| 879 | # echo "No Windows updater manifests found to merge." >&2 | |
| 880 | # exit 1 | |
| 881 | # fi | |
| 882 | ||
| 883 | 854 | - name: Publish release |
| 884 | 855 | if: needs.preflight.outputs.previous_tag != '' |
| 885 | 856 | uses: softprops/action-gh-release@v3 |
apps/desktop/src/wsl/DesktopWslEnvironment.test.ts-3| ... | ... | @@ -198,13 +198,10 @@ describe("WSL runtime cache", () => { |
| 198 | 198 | expect(script).toContain(' [ -f "$runtime_root/apps/server/dist/bin.mjs" ] &&'); |
| 199 | 199 | expect(script).toContain(' [ -f "$runtime_root/node_modules/node-pty/package.json" ] &&'); |
| 200 | 200 | expect(script).toContain(' node_pty_payload_present "$runtime_root"'); |
| 201 | expect(script).not.toContain("node_modules/effect/package.json"); | |
| 202 | 201 | expect(script).toContain("if runtime_is_ready; then"); |
| 203 | 202 | expect(script).toContain("trap 'exit 1' HUP INT TERM"); |
| 204 | 203 | expect(script).toContain('exec 9> "$runtime_lock"'); |
| 205 | 204 | expect(script).toContain("flock -x 9"); |
| 206 | expect(script).not.toContain("runtime_lock_pid"); | |
| 207 | expect(script).not.toContain("sleep 0.1"); | |
| 208 | 205 | expect(script).not.toContain('rm -rf "$runtime_lock"'); |
| 209 | 206 | expect(script).toContain('mv -T "$runtime_root" "$runtime_stale"'); |
| 210 | 207 | expect(script).toContain('mktemp -d "$runtime_parent/.1.2.3-x64.tmp.XXXXXX"'); |
apps/marketing/public/apple-touch-icon.webp deleted| Binary files a/apps/marketing/public/apple-touch-icon.webp and /dev/null differ |
apps/marketing/public/favicon-16x16.webp deleted| Binary files a/apps/marketing/public/favicon-16x16.webp and /dev/null differ |
apps/marketing/public/favicon-32x32.webp deleted| Binary files a/apps/marketing/public/favicon-32x32.webp and /dev/null differ |
apps/marketing/public/icon.png deleted| Binary files a/apps/marketing/public/icon.png and /dev/null differ |
apps/marketing/public/screenshot.webp deleted| Binary files a/apps/marketing/public/screenshot.webp and /dev/null differ |
apps/marketing/tweets.md deleted-19| ... | ... | @@ -1,19 +0,0 @@ |
| 1 | https://x.com/Shay_Benshabtay/status/2054668503857156326 | |
| 2 | https://x.com/teja2495/status/2052420254991581623 | |
| 3 | https://x.com/developedbyed/status/2030627970532921605 | |
| 4 | https://x.com/tannerlinsley/status/2031102771529920966 | |
| 5 | https://x.com/aronprins/status/2045102518196183109 | |
| 6 | https://x.com/BennettBuhner/status/2054667115697754387 | |
| 7 | https://x.com/ex0t1clol/status/2054666870008021197 | |
| 8 | https://x.com/Josikinz/status/2030367951694745870 | |
| 9 | https://x.com/jetpackjoe_/status/2054666792933404959 | |
| 10 | https://x.com/mil000/status/2030120041451246071 | |
| 11 | https://x.com/_winter_wonders/status/2052350198764970434 | |
| 12 | https://x.com/kostyniuk00/status/2052041388179468521 | |
| 13 | https://x.com/gnukeith/status/2054670073579630730 | |
| 14 | https://x.com/peculiarnewbie/status/2054671685027233827 | |
| 15 | https://x.com/leodev/status/2054679746353537042 | |
| 16 | https://x.com/pocarles/status/2054673964274758046 | |
| 17 | https://x.com/DavidKPiano/status/2054682983504719930 | |
| 18 | https://x.com/iamkaffe/status/2054675539311411280 | |
| 19 | https://x.com/uwunetes/status/2054683356022120640 |
apps/mobile/package.json-1| ... | ... | @@ -52,7 +52,6 @@ |
| 52 | 52 | "@expo/ui": "~57.0.14", |
| 53 | 53 | "@legendapp/list": "catalog:", |
| 54 | 54 | "@noble/curves": "catalog:", |
| 55 | "@noble/hashes": "catalog:", | |
| 56 | 55 | "@pierre/diffs": "catalog:", |
| 57 | 56 | "@react-native-ai/apple": "0.12.0", |
| 58 | 57 | "@react-native-menu/menu": "^2.0.0", |
apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.test.ts deleted-108| ... | ... | @@ -1,108 +0,0 @@ |
| 1 | import { describe, expect, it } from "vite-plus/test"; | |
| 2 | ||
| 3 | import { resolveTerminalRouteBootstrap } from "./terminalRouteBootstrap"; | |
| 4 | ||
| 5 | describe("resolveTerminalRouteBootstrap", () => { | |
| 6 | it("redirects bare terminal routes to another already-running terminal for the thread", () => { | |
| 7 | expect( | |
| 8 | resolveTerminalRouteBootstrap({ | |
| 9 | hasThread: true, | |
| 10 | hasWorkspaceRoot: true, | |
| 11 | hasOpened: false, | |
| 12 | requestedTerminalId: null, | |
| 13 | currentTerminalId: "default", | |
| 14 | runningTerminalId: "term-2", | |
| 15 | currentTerminalStatus: "closed", | |
| 16 | hasCurrentTerminalHydration: false, | |
| 17 | }), | |
| 18 | ).toEqual({ | |
| 19 | kind: "redirect", | |
| 20 | terminalId: "term-2", | |
| 21 | }); | |
| 22 | }); | |
| 23 | ||
| 24 | it("hydrates the current running terminal when client state is not hydrated yet", () => { | |
| 25 | expect( | |
| 26 | resolveTerminalRouteBootstrap({ | |
| 27 | hasThread: true, | |
| 28 | hasWorkspaceRoot: true, | |
| 29 | hasOpened: false, | |
| 30 | requestedTerminalId: null, | |
| 31 | currentTerminalId: "default", | |
| 32 | runningTerminalId: "default", | |
| 33 | currentTerminalStatus: "running", | |
| 34 | hasCurrentTerminalHydration: false, | |
| 35 | }), | |
| 36 | ).toEqual({ | |
| 37 | kind: "open", | |
| 38 | }); | |
| 39 | }); | |
| 40 | ||
| 41 | it("opens explicit terminal routes when the session still needs hydration", () => { | |
| 42 | expect( | |
| 43 | resolveTerminalRouteBootstrap({ | |
| 44 | hasThread: true, | |
| 45 | hasWorkspaceRoot: true, | |
| 46 | hasOpened: false, | |
| 47 | requestedTerminalId: "term-2", | |
| 48 | currentTerminalId: "term-2", | |
| 49 | runningTerminalId: "term-2", | |
| 50 | currentTerminalStatus: "running", | |
| 51 | hasCurrentTerminalHydration: false, | |
| 52 | }), | |
| 53 | ).toEqual({ | |
| 54 | kind: "open", | |
| 55 | }); | |
| 56 | }); | |
| 57 | ||
| 58 | it("stays idle after the route already bootstrapped once", () => { | |
| 59 | expect( | |
| 60 | resolveTerminalRouteBootstrap({ | |
| 61 | hasThread: true, | |
| 62 | hasWorkspaceRoot: true, | |
| 63 | hasOpened: true, | |
| 64 | requestedTerminalId: null, | |
| 65 | currentTerminalId: "default", | |
| 66 | runningTerminalId: "default", | |
| 67 | currentTerminalStatus: "running", | |
| 68 | hasCurrentTerminalHydration: true, | |
| 69 | }), | |
| 70 | ).toEqual({ | |
| 71 | kind: "idle", | |
| 72 | }); | |
| 73 | }); | |
| 74 | ||
| 75 | it("stays idle when the current running terminal is already hydrated in client state", () => { | |
| 76 | expect( | |
| 77 | resolveTerminalRouteBootstrap({ | |
| 78 | hasThread: true, | |
| 79 | hasWorkspaceRoot: true, | |
| 80 | hasOpened: false, | |
| 81 | requestedTerminalId: null, | |
| 82 | currentTerminalId: "default", | |
| 83 | runningTerminalId: "default", | |
| 84 | currentTerminalStatus: "running", | |
| 85 | hasCurrentTerminalHydration: true, | |
| 86 | }), | |
| 87 | ).toEqual({ | |
| 88 | kind: "idle", | |
| 89 | }); | |
| 90 | }); | |
| 91 | ||
| 92 | it("stays idle for explicit running terminal routes that already have hydrated output", () => { | |
| 93 | expect( | |
| 94 | resolveTerminalRouteBootstrap({ | |
| 95 | hasThread: true, | |
| 96 | hasWorkspaceRoot: true, | |
| 97 | hasOpened: false, | |
| 98 | requestedTerminalId: "term-2", | |
| 99 | currentTerminalId: "term-2", | |
| 100 | runningTerminalId: "term-2", | |
| 101 | currentTerminalStatus: "running", | |
| 102 | hasCurrentTerminalHydration: true, | |
| 103 | }), | |
| 104 | ).toEqual({ | |
| 105 | kind: "idle", | |
| 106 | }); | |
| 107 | }); | |
| 108 | }); |
apps/mobile/src/features/terminal/terminalRouteBootstrap.ts deleted-35| ... | ... | @@ -1,35 +0,0 @@ |
| 1 | export function resolveTerminalRouteBootstrap(input: { | |
| 2 | readonly hasThread: boolean; | |
| 3 | readonly hasWorkspaceRoot: boolean; | |
| 4 | readonly hasOpened: boolean; | |
| 5 | readonly requestedTerminalId: string | null; | |
| 6 | readonly currentTerminalId: string; | |
| 7 | readonly runningTerminalId: string | null; | |
| 8 | readonly currentTerminalStatus: "starting" | "running" | "exited" | "error" | "closed"; | |
| 9 | /** True once the attach stream has populated scrollback (`buffer` non-empty), not merely metadata. */ | |
| 10 | readonly hasCurrentTerminalHydration: boolean; | |
| 11 | }): | |
| 12 | | { readonly kind: "idle" } | |
| 13 | | { readonly kind: "redirect"; readonly terminalId: string } | |
| 14 | | { readonly kind: "open" } { | |
| 15 | if (!input.hasThread || !input.hasWorkspaceRoot || input.hasOpened) { | |
| 16 | return { kind: "idle" }; | |
| 17 | } | |
| 18 | ||
| 19 | if ( | |
| 20 | input.requestedTerminalId === null && | |
| 21 | input.runningTerminalId !== null && | |
| 22 | input.runningTerminalId !== input.currentTerminalId | |
| 23 | ) { | |
| 24 | return { kind: "redirect", terminalId: input.runningTerminalId }; | |
| 25 | } | |
| 26 | ||
| 27 | if ( | |
| 28 | (input.currentTerminalStatus === "running" || input.currentTerminalStatus === "starting") && | |
| 29 | input.hasCurrentTerminalHydration | |
| 30 | ) { | |
| 31 | return { kind: "idle" }; | |
| 32 | } | |
| 33 | ||
| 34 | return { kind: "open" }; | |
| 35 | } |
apps/server/integration/OrchestrationEngineHarness.integration.ts+2-2| ... | ... | @@ -25,7 +25,7 @@ import * as Stream from "effect/Stream"; |
| 25 | 25 | import * as Tracer from "effect/Tracer"; |
| 26 | 26 | |
| 27 | 27 | import * as CheckpointStore from "../src/checkpointing/CheckpointStore.ts"; |
| 28 | import { TextGeneration, type TextGenerationShape } from "../src/textGeneration/TextGeneration.ts"; | |
| 28 | import { TextGeneration } from "../src/textGeneration/TextGeneration.ts"; | |
| 29 | 29 | import { OrchestrationCommandReceiptRepositoryLive } from "../src/persistence/Layers/OrchestrationCommandReceipts.ts"; |
| 30 | 30 | import { OrchestrationEventStoreLive } from "../src/persistence/Layers/OrchestrationEventStore.ts"; |
| 31 | 31 | import { ProjectionCheckpointRepositoryLive } from "../src/persistence/Layers/ProjectionCheckpoints.ts"; |
| ... | ... | @@ -335,7 +335,7 @@ export const makeOrchestrationIntegrationHarness = ( |
| 335 | 335 | const textGenerationLayer = Layer.succeed(TextGeneration, { |
| 336 | 336 | generateBranchName: () => Effect.succeed({ branch: "update" }), |
| 337 | 337 | generateThreadTitle: () => Effect.succeed({ title: "New thread" }), |
| 338 | } as unknown as TextGenerationShape); | |
| 338 | } as unknown as TextGeneration["Service"]); | |
| 339 | 339 | const providerCommandReactorLayer = ProviderCommandReactorLive.pipe( |
| 340 | 340 | Layer.provideMerge(runtimeServicesLayer), |
| 341 | 341 | Layer.provideMerge(gitWorkflowLayer), |
apps/server/package.json-1| ... | ... | @@ -25,7 +25,6 @@ |
| 25 | 25 | "@anthropic-ai/claude-agent-sdk": "^0.3.170", |
| 26 | 26 | "@effect/platform-bun": "catalog:", |
| 27 | 27 | "@effect/platform-node": "catalog:", |
| 28 | "@effect/platform-node-shared": "catalog:", | |
| 29 | 28 | "@effect/sql-sqlite-bun": "catalog:", |
| 30 | 29 | "@ff-labs/fff-node": "0.9.4", |
| 31 | 30 | "@opencode-ai/sdk": "^1.3.15", |
apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts+3-3| ... | ... | @@ -45,7 +45,7 @@ import { |
| 45 | 45 | type ProviderServiceShape, |
| 46 | 46 | } from "../../provider/Services/ProviderService.ts"; |
| 47 | 47 | import { makeProviderRegistryLayer } from "../../provider/testUtils/providerRegistryMock.ts"; |
| 48 | import { TextGeneration, type TextGenerationShape } from "../../textGeneration/TextGeneration.ts"; | |
| 48 | import { TextGeneration } from "../../textGeneration/TextGeneration.ts"; | |
| 49 | 49 | import * as RepositoryIdentityResolver from "../../project/RepositoryIdentityResolver.ts"; |
| 50 | 50 | import { OrchestrationEngineLive } from "./OrchestrationEngine.ts"; |
| 51 | 51 | import { OrchestrationProjectionPipelineLive } from "./ProjectionPipeline.ts"; |
| ... | ... | @@ -310,7 +310,7 @@ describe("ProviderCommandReactor", () => { |
| 310 | 310 | pr: null, |
| 311 | 311 | }), |
| 312 | 312 | ); |
| 313 | const generateBranchName = vi.fn<TextGenerationShape["generateBranchName"]>((_) => | |
| 313 | const generateBranchName = vi.fn<TextGeneration["Service"]["generateBranchName"]>((_) => | |
| 314 | 314 | Effect.fail( |
| 315 | 315 | new TextGenerationError({ |
| 316 | 316 | operation: "generateBranchName", |
| ... | ... | @@ -318,7 +318,7 @@ describe("ProviderCommandReactor", () => { |
| 318 | 318 | }), |
| 319 | 319 | ), |
| 320 | 320 | ); |
| 321 | const generateThreadTitle = vi.fn<TextGenerationShape["generateThreadTitle"]>((_) => | |
| 321 | const generateThreadTitle = vi.fn<TextGeneration["Service"]["generateThreadTitle"]>((_) => | |
| 322 | 322 | Effect.fail( |
| 323 | 323 | new TextGenerationError({ |
| 324 | 324 | operation: "generateThreadTitle", |
apps/server/src/provider/Layers/ProviderAdapterRegistry.test.ts-13| ... | ... | @@ -96,11 +96,6 @@ const fakeCursorAdapter: CursorAdapter.CursorAdapterShape = { |
| 96 | 96 | streamEvents: Stream.empty, |
| 97 | 97 | }; |
| 98 | 98 | |
| 99 | // ProviderAdapterRegistryLive is now a facade over ProviderInstanceRegistry — | |
| 100 | // it walks `listInstances` once at boot and surfaces the default-instance | |
| 101 | // adapter keyed by its driver kind. To test the facade we supply four fake | |
| 102 | // instances whose `instanceId === defaultInstanceIdForDriver(driverKind)` so | |
| 103 | // they pass the default-instance filter. | |
| 104 | 99 | const makeFakeInstance = ( |
| 105 | 100 | driverKindString: "codex" | "claudeAgent" | "cursor" | "opencode", |
| 106 | 101 | adapter: ProviderInstance["adapter"], |
| ... | ... | @@ -184,13 +179,5 @@ it.layer(layer)("ProviderAdapterRegistryLive", (it) => { |
| 184 | 179 | defaultInstanceIdForDriver(OPENCODE_DRIVER), |
| 185 | 180 | defaultInstanceIdForDriver(CURSOR_DRIVER), |
| 186 | 181 | ]); |
| 187 | ||
| 188 | const providers = yield* registry.listProviders(); | |
| 189 | assert.deepStrictEqual(providers, [ | |
| 190 | CODEX_DRIVER, | |
| 191 | CLAUDE_AGENT_DRIVER, | |
| 192 | OPENCODE_DRIVER, | |
| 193 | CURSOR_DRIVER, | |
| 194 | ]); | |
| 195 | 182 | })); |
| 196 | 183 | }); |
apps/server/src/provider/Layers/ProviderAdapterRegistry.ts+1-26| ... | ... | @@ -15,11 +15,7 @@ |
| 15 | 15 | * |
| 16 | 16 | * @module ProviderAdapterRegistryLive |
| 17 | 17 | */ |
| 18 | import { | |
| 19 | defaultInstanceIdForDriver, | |
| 20 | ProviderInstanceId, | |
| 21 | type ProviderDriverKind, | |
| 22 | } from "@t3tools/contracts"; | |
| 18 | import { ProviderInstanceId } from "@t3tools/contracts"; | |
| 23 | 19 | import * as Effect from "effect/Effect"; |
| 24 | 20 | import * as Layer from "effect/Layer"; |
| 25 | 21 | |
| ... | ... | @@ -71,31 +67,10 @@ const makeProviderAdapterRegistry = Effect.fn("makeProviderAdapterRegistry")(fun |
| 71 | 67 | Effect.map((instances) => instances.map((instance) => instance.instanceId)), |
| 72 | 68 | ); |
| 73 | 69 | |
| 74 | const listProviders: ProviderAdapterRegistryShape["listProviders"] = () => | |
| 75 | registry.listInstances.pipe( | |
| 76 | Effect.map((instances) => { | |
| 77 | const kinds = new Set<ProviderDriverKind>(); | |
| 78 | for (const instance of instances) { | |
| 79 | const defaultId = defaultInstanceIdForDriver(instance.driverKind); | |
| 80 | if (instance.instanceId === defaultId) { | |
| 81 | // Only the default-instance rows show up through the legacy | |
| 82 | // shim — custom instances like `codex_personal` have no | |
| 83 | // `ProviderDriverKind` equivalent. | |
| 84 | kinds.add(instance.driverKind); | |
| 85 | } | |
| 86 | } | |
| 87 | return Array.from(kinds); | |
| 88 | }), | |
| 89 | ); | |
| 90 | ||
| 91 | 70 | return { |
| 92 | 71 | getByInstance, |
| 93 | 72 | getInstanceInfo, |
| 94 | 73 | listInstances, |
| 95 | listProviders, | |
| 96 | // Proxy directly — the facade has no state of its own; the instance | |
| 97 | // registry already coalesces adds/removes/rebuilds into one emission. | |
| 98 | streamChanges: registry.streamChanges, | |
| 99 | 74 | subscribeChanges: registry.subscribeChanges, |
| 100 | 75 | } satisfies ProviderAdapterRegistryShape; |
| 101 | 76 | }); |
apps/server/src/provider/Layers/ProviderService.test.ts-4| ... | ... | @@ -514,8 +514,6 @@ it.effect( |
| 514 | 514 | }) |
| 515 | 515 | : Effect.fail(unsupported()), |
| 516 | 516 | listInstances: () => Effect.succeed([instanceId]), |
| 517 | listProviders: () => Effect.succeed([driverKind] as const), | |
| 518 | streamChanges: Stream.empty, | |
| 519 | 517 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) => |
| 520 | 518 | PubSub.subscribe(pubsub), |
| 521 | 519 | ), |
| ... | ... | @@ -594,8 +592,6 @@ it.effect("ProviderServiceLive rejects new sessions for disabled custom instance |
| 594 | 592 | }) |
| 595 | 593 | : Effect.fail(unsupported()), |
| 596 | 594 | listInstances: () => Effect.succeed([instanceId]), |
| 597 | listProviders: () => Effect.succeed([CODEX_DRIVER] as const), | |
| 598 | streamChanges: Stream.empty, | |
| 599 | 595 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) => |
| 600 | 596 | PubSub.subscribe(pubsub), |
| 601 | 597 | ), |
apps/server/src/provider/Services/ProviderAdapterRegistry.ts+3-35| ... | ... | @@ -1,21 +1,9 @@ |
| 1 | 1 | /** |
| 2 | 2 | * ProviderAdapterRegistry - Lookup boundary for provider adapter implementations. |
| 3 | 3 | * |
| 4 | * Maps a `ProviderInstanceId` (the new per-instance routing key) or a | |
| 5 | * `ProviderDriverKind` (legacy single-instance-per-driver key) to the concrete | |
| 6 | * adapter service (Codex, Claude, etc). It does not own session lifecycle | |
| 7 | * or routing rules; `ProviderService` uses this registry together with | |
| 8 | * `ProviderSessionDirectory`. | |
| 9 | * | |
| 10 | * During the driver/instance migration this tag exposes both flavours: | |
| 11 | * | |
| 12 | * - `getByInstance` / `listInstances` — new per-instance routing. Callers | |
| 13 | * that already know an `instanceId` (threads, sessions, events) | |
| 14 | * should prefer these. | |
| 15 | * (`defaultInstanceIdForDriver(kind) === kind`), matching the pre-Slice-D | |
| 16 | * behaviour. New code should not grow additional callers of the kind-keyed | |
| 17 | * methods; they exist so the settings UI, WS refresh RPC, and a handful | |
| 18 | * of legacy persisted rows can still be routed during the rollout. | |
| 4 | * Maps a `ProviderInstanceId` to its provider adapter. `ProviderService` uses | |
| 5 | * this registry with `ProviderSessionDirectory`. The registry does not own | |
| 6 | * session lifecycle or routing rules. | |
| 19 | 7 | * |
| 20 | 8 | * @module ProviderAdapterRegistry |
| 21 | 9 | */ |
| ... | ... | @@ -24,7 +12,6 @@ import * as Context from "effect/Context"; |
| 24 | 12 | import type * as Effect from "effect/Effect"; |
| 25 | 13 | import type * as PubSub from "effect/PubSub"; |
| 26 | 14 | import type * as Scope from "effect/Scope"; |
| 27 | import type * as Stream from "effect/Stream"; | |
| 28 | 15 | |
| 29 | 16 | import type { ProviderAdapterError, ProviderUnsupportedError } from "../Errors.ts"; |
| 30 | 17 | import type { ProviderAdapterShape } from "./ProviderAdapter.ts"; |
| ... | ... | @@ -64,25 +51,6 @@ export interface ProviderAdapterRegistryShape { |
| 64 | 51 | */ |
| 65 | 52 | readonly listInstances: () => Effect.Effect<ReadonlyArray<ProviderInstanceId>>; |
| 66 | 53 | |
| 67 | /** | |
| 68 | * Legacy: list provider kinds whose default instance is currently | |
| 69 | * registered. | |
| 70 | * | |
| 71 | * @deprecated Prefer `listInstances`. Retained for migration-era call | |
| 72 | * sites that iterate providers to build UI/metrics. | |
| 73 | */ | |
| 74 | readonly listProviders: () => Effect.Effect<ReadonlyArray<ProviderDriverKind>>; | |
| 75 | ||
| 76 | /** | |
| 77 | * Change notification stream mirroring `ProviderInstanceRegistry.streamChanges`. | |
| 78 | * Emits one `void` tick whenever the set of live instances changes | |
| 79 | * (instance added, removed, or rebuilt after a settings edit). Consumers | |
| 80 | * that fan out `adapter.streamEvents` per instance — e.g. `ProviderService`'s | |
| 81 | * runtime event bus — re-pull `listInstances` on each tick and fork new | |
| 82 | * subscriptions for instances they haven't seen yet. | |
| 83 | */ | |
| 84 | readonly streamChanges: Stream.Stream<void>; | |
| 85 | ||
| 86 | 54 | /** |
| 87 | 55 | * Acquire a change subscription synchronously in the caller's current fiber. |
| 88 | 56 | * Consumers that must avoid missing a publish between initial reconciliation |
apps/server/src/provider/testUtils/providerAdapterRegistryMock.ts+2-27| ... | ... | @@ -2,15 +2,6 @@ |
| 2 | 2 | * Test helpers for constructing a `ProviderAdapterRegistryShape` mock from a |
| 3 | 3 | * kind-keyed adapter map. |
| 4 | 4 | * |
| 5 | * Tests historically assembled a `registry` object with only `getByProvider` | |
| 6 | * + `listProviders` populated. Slice D grew the shape with `getByInstance` | |
| 7 | * and `listInstances`; this helper fills both in from a single kind-keyed | |
| 8 | * input so individual fixtures can stay concise. | |
| 9 | * | |
| 10 | * Non-default instance ids (e.g. `codex_personal`) are not addressable via | |
| 11 | * the shim returned here — the legacy test fixtures only ever had | |
| 12 | * single-instance-per-driver data anyway. | |
| 13 | * | |
| 14 | 5 | * @module provider/testUtils/providerAdapterRegistryMock |
| 15 | 6 | */ |
| 16 | 7 | import { |
| ... | ... | @@ -20,9 +11,6 @@ import { |
| 20 | 11 | } from "@t3tools/contracts"; |
| 21 | 12 | import * as Effect from "effect/Effect"; |
| 22 | 13 | import * as PubSub from "effect/PubSub"; |
| 23 | import * as Record from "effect/Record"; | |
| 24 | import * as Result from "effect/Result"; | |
| 25 | import * as Stream from "effect/Stream"; | |
| 26 | 14 | |
| 27 | 15 | import { ProviderUnsupportedError, type ProviderAdapterError } from "../Errors.ts"; |
| 28 | 16 | import type { ProviderAdapterShape } from "../Services/ProviderAdapter.ts"; |
| ... | ... | @@ -34,9 +22,8 @@ export type KindAdapterMap = Partial< |
| 34 | 22 | |
| 35 | 23 | /** |
| 36 | 24 | * Build a `ProviderAdapterRegistryShape` from a kind-keyed adapter map. |
| 37 | * Every adapter present in the map is addressable via both the legacy | |
| 38 | * `getByProvider(kind)` path and the new `getByInstance(id)` path (where | |
| 39 | * `id = defaultInstanceIdForDriver(kind)`). | |
| 25 | * Every adapter present in the map is addressable through its default | |
| 26 | * provider instance id. | |
| 40 | 27 | */ |
| 41 | 28 | export const makeAdapterRegistryMock = (adapters: KindAdapterMap): ProviderAdapterRegistryShape => { |
| 42 | 29 | const byInstanceId = new Map<ProviderInstanceId, ProviderAdapterShape<ProviderAdapterError>>(); |
| ... | ... | @@ -80,18 +67,6 @@ export const makeAdapterRegistryMock = (adapters: KindAdapterMap): ProviderAdapt |
| 80 | 67 | }); |
| 81 | 68 | }, |
| 82 | 69 | listInstances: () => Effect.succeed(Array.from(byInstanceId.keys())), |
| 83 | listProviders: () => | |
| 84 | Effect.succeed( | |
| 85 | Record.keys( | |
| 86 | Record.filterMap(adapters, (adapter, kind) => | |
| 87 | adapter !== undefined ? Result.succeed(kind) : Result.failVoid, | |
| 88 | ), | |
| 89 | ), | |
| 90 | ), | |
| 91 | // Static test fixtures don't reload; an empty stream is enough to | |
| 92 | // satisfy the shape. Tests exercising hot-reload build their own | |
| 93 | // stream via the real `ProviderInstanceRegistry`. | |
| 94 | streamChanges: Stream.empty, | |
| 95 | 70 | subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) => |
| 96 | 71 | PubSub.subscribe(pubsub), |
| 97 | 72 | ), |
apps/server/src/textGeneration/TextGeneration.ts-12| ... | ... | @@ -73,15 +73,6 @@ export interface ThreadTitleGenerationResult { |
| 73 | 73 | title: string; |
| 74 | 74 | } |
| 75 | 75 | |
| 76 | export interface TextGenerationService { | |
| 77 | generateCommitMessage( | |
| 78 | input: CommitMessageGenerationInput, | |
| 79 | ): Promise<CommitMessageGenerationResult>; | |
| 80 | generatePrContent(input: PrContentGenerationInput): Promise<PrContentGenerationResult>; | |
| 81 | generateBranchName(input: BranchNameGenerationInput): Promise<BranchNameGenerationResult>; | |
| 82 | generateThreadTitle(input: ThreadTitleGenerationInput): Promise<ThreadTitleGenerationResult>; | |
| 83 | } | |
| 84 | ||
| 85 | 76 | /** |
| 86 | 77 | * TextGeneration - Service tag for commit and change request text generation. |
| 87 | 78 | */ |
| ... | ... | @@ -116,9 +107,6 @@ export class TextGeneration extends Context.Service< |
| 116 | 107 | } |
| 117 | 108 | >()("t3/textGeneration/TextGeneration") {} |
| 118 | 109 | |
| 119 | /** @deprecated Use `TextGeneration["Service"]`. */ | |
| 120 | export type TextGenerationShape = TextGeneration["Service"]; | |
| 121 | ||
| 122 | 110 | type TextGenerationOp = |
| 123 | 111 | | "generateCommitMessage" |
| 124 | 112 | | "generatePrContent" |
apps/web/src/cloud/linkEnvironment.ts-45| ... | ... | @@ -16,7 +16,6 @@ import { |
| 16 | 16 | WS_METHODS, |
| 17 | 17 | } from "@t3tools/contracts"; |
| 18 | 18 | import { |
| 19 | type RelayClientDeviceRecord, | |
| 20 | 19 | type RelayClientEnvironmentRecord, |
| 21 | 20 | type RelayEnvironmentLinkResponse, |
| 22 | 21 | type RelayManagedEndpointProviderKind, |
| ... | ... | @@ -26,10 +25,6 @@ import { request, runStream } from "@t3tools/client-runtime/rpc"; |
| 26 | 25 | import { makeEnvironmentHttpApiClient } from "@t3tools/client-runtime/rpc"; |
| 27 | 26 | import { ManagedRelay, relayProtectedErrorMessage } from "@t3tools/client-runtime/relay"; |
| 28 | 27 | |
| 29 | import { | |
| 30 | readPrimaryEnvironmentDescriptor, | |
| 31 | readPrimaryEnvironmentTarget, | |
| 32 | } from "../environments/primary"; | |
| 33 | 28 | import { primaryEnvironmentHttpLayer } from "../environments/primary/httpLayer"; |
| 34 | 29 | import { resolveCloudPublicConfig } from "./publicConfig"; |
| 35 | 30 | import { |
| ... | ... | @@ -215,20 +210,6 @@ export function collectCloudLinkTargets(input: { |
| 215 | 210 | return [...byId.values()]; |
| 216 | 211 | } |
| 217 | 212 | |
| 218 | export function readPrimaryCloudLinkTarget(): CloudLinkTarget | null { | |
| 219 | const descriptor = readPrimaryEnvironmentDescriptor(); | |
| 220 | const target = readPrimaryEnvironmentTarget(); | |
| 221 | if (!descriptor || !target) { | |
| 222 | return null; | |
| 223 | } | |
| 224 | return { | |
| 225 | environmentId: descriptor.environmentId, | |
| 226 | label: descriptor.label, | |
| 227 | httpBaseUrl: target.target.httpBaseUrl, | |
| 228 | wsBaseUrl: target.target.wsBaseUrl, | |
| 229 | }; | |
| 230 | } | |
| 231 | ||
| 232 | 213 | export function listManagedCloudEnvironments(input: { |
| 233 | 214 | readonly clerkToken: string; |
| 234 | 215 | }): Effect.Effect< |
| ... | ... | @@ -260,32 +241,6 @@ export function listManagedCloudEnvironments(input: { |
| 260 | 241 | }); |
| 261 | 242 | } |
| 262 | 243 | |
| 263 | export function listCloudDevices(input: { | |
| 264 | readonly clerkToken: string; | |
| 265 | }): Effect.Effect< | |
| 266 | ReadonlyArray<RelayClientDeviceRecord>, | |
| 267 | CloudEnvironmentLinkError, | |
| 268 | ManagedRelay.ManagedRelayClient | |
| 269 | > { | |
| 270 | return Effect.gen(function* () { | |
| 271 | if (!relayUrl()) { | |
| 272 | return yield* new CloudEnvironmentLinkError({ | |
| 273 | message: "T3CODE_RELAY_URL is not configured.", | |
| 274 | }); | |
| 275 | } | |
| 276 | const relayClient = yield* ManagedRelay.ManagedRelayClient; | |
| 277 | return yield* relayClient.listDevices({ clerkToken: input.clerkToken }).pipe( | |
| 278 | Effect.mapError( | |
| 279 | (cause) => | |
| 280 | new CloudEnvironmentLinkError({ | |
| 281 | message: "Could not list cloud devices.", | |
| 282 | cause, | |
| 283 | }), | |
| 284 | ), | |
| 285 | ); | |
| 286 | }); | |
| 287 | } | |
| 288 | ||
| 289 | 244 | export function readPrimaryCloudLinkState(input: { |
| 290 | 245 | readonly target: CloudLinkTarget; |
| 291 | 246 | }): Effect.Effect<CloudLinkState | null, CloudEnvironmentLinkError, HttpClient.HttpClient> { |
apps/web/src/components/DiffPanelShell.tsx-14| ... | ... | @@ -46,20 +46,6 @@ export function DiffPanelShell(props: { |
| 46 | 46 | ); |
| 47 | 47 | } |
| 48 | 48 | |
| 49 | export function DiffPanelHeaderSkeleton() { | |
| 50 | return ( | |
| 51 | <> | |
| 52 | <div className="min-w-0 flex-1"> | |
| 53 | <Skeleton className="h-8 w-32 rounded-lg" /> | |
| 54 | </div> | |
| 55 | <div className="flex shrink-0 gap-1"> | |
| 56 | <Skeleton className="size-7 rounded-md" /> | |
| 57 | <Skeleton className="size-7 rounded-md" /> | |
| 58 | </div> | |
| 59 | </> | |
| 60 | ); | |
| 61 | } | |
| 62 | ||
| 63 | 49 | function DiffFileHeaderSkeleton({ titleClassName }: { titleClassName: string }) { |
| 64 | 50 | return ( |
| 65 | 51 | <div className="flex h-8 items-center gap-2 px-2 pr-3"> |
apps/web/src/components/ProviderUpdateLaunchNotification.logic.ts-4| ... | ... | @@ -202,10 +202,6 @@ export function providerUpdateNotificationKey( |
| 202 | 202 | return parts.length > 0 ? parts.join("|") : null; |
| 203 | 203 | } |
| 204 | 204 | |
| 205 | export function providerUpdateCandidateKey(provider: ProviderUpdateCandidate): string { | |
| 206 | return providerUpdateNotificationKey([provider])!; | |
| 207 | } | |
| 208 | ||
| 209 | 205 | export function formatProviderList(providers: ReadonlyArray<Pick<ServerProvider, "driver">>) { |
| 210 | 206 | const names = providers.map( |
| 211 | 207 | (provider) => PROVIDER_DISPLAY_NAMES[provider.driver] ?? provider.driver, |
apps/web/src/components/chat/ChangedFilesTree.test.tsx-11| ... | ... | @@ -22,17 +22,6 @@ describe("ChangedFilesCard", () => { |
| 22 | 22 | |
| 23 | 23 | expect(markup).toContain('data-changed-files-state="expanded"'); |
| 24 | 24 | expect(markup).toContain('aria-expanded="true"'); |
| 25 | expect(markup).toContain("whitespace-nowrap"); | |
| 26 | expect(markup).toContain('class="group flex min-w-0 flex-1 items-center rounded-xl'); | |
| 27 | expect(markup).not.toMatch(/class="group flex min-w-0 flex-1 items-center[^"]*overflow-hidden/); | |
| 28 | expect(markup).toContain('class="flex min-w-0 flex-1 items-center gap-1.5 overflow-hidden"'); | |
| 29 | expect(markup).toContain('class="flex shrink-0 items-center gap-1 whitespace-nowrap'); | |
| 30 | expect(markup).toContain('class="ml-1 hidden min-w-0 flex-1 truncate'); | |
| 31 | expect(markup).toContain("@[24rem]/changed-files:inline"); | |
| 32 | expect(markup).not.toContain("sm:inline"); | |
| 33 | expect(markup).toContain('class="flex shrink-0 items-center gap-1.5 pr-1"'); | |
| 34 | expect(markup).toContain("!size-[22px]"); | |
| 35 | expect(markup).toContain("size-3"); | |
| 36 | 25 | expect(markup).toContain('aria-label="Collapse all folders"'); |
| 37 | 26 | expect(markup).toContain('aria-label="Open diff"'); |
| 38 | 27 | expect(markup).toContain('role="group" aria-label="2 additions, 1 deletions"'); |
apps/web/src/components/chat/ComposerCommandMenu.test.tsx+3-36| ... | ... | @@ -5,24 +5,7 @@ import { describe, expect, it } from "vite-plus/test"; |
| 5 | 5 | import { ComposerCommandMenu } from "./ComposerCommandMenu"; |
| 6 | 6 | |
| 7 | 7 | describe("ComposerCommandMenu", () => { |
| 8 | it("renders slash-command results as an attached composer drawer", () => { | |
| 9 | const markup = renderToStaticMarkup( | |
| 10 | <ComposerCommandMenu | |
| 11 | items={[]} | |
| 12 | resolvedTheme="dark" | |
| 13 | isLoading={false} | |
| 14 | triggerKind="slash-command" | |
| 15 | activeItemId={null} | |
| 16 | onHighlightedItemChange={() => {}} | |
| 17 | onSelect={() => {}} | |
| 18 | />, | |
| 19 | ); | |
| 20 | ||
| 21 | expect(markup).toContain('data-composer-command-drawer="true"'); | |
| 22 | expect(markup).not.toContain("dropdown-glass"); | |
| 23 | }); | |
| 24 | ||
| 25 | it("renders commands without a category heading or invented icons", () => { | |
| 8 | it("renders slash commands with their descriptions", () => { | |
| 26 | 9 | const markup = renderToStaticMarkup( |
| 27 | 10 | <ComposerCommandMenu |
| 28 | 11 | items={[ |
| ... | ... | @@ -45,16 +28,9 @@ describe("ComposerCommandMenu", () => { |
| 45 | 28 | |
| 46 | 29 | expect(markup).toContain("/model"); |
| 47 | 30 | expect(markup).toContain("Switch response model for this thread"); |
| 48 | expect(markup).not.toContain("Built-in"); | |
| 49 | expect(markup).not.toContain("<svg"); | |
| 50 | expect(markup).toContain("font-sans text-xs font-medium"); | |
| 51 | expect(markup).not.toContain("font-mono"); | |
| 52 | expect(markup).not.toContain("grid-cols-"); | |
| 53 | expect(markup).toContain("max-w-[45%]"); | |
| 54 | expect(markup).toContain("text-left"); | |
| 55 | 31 | }); |
| 56 | 32 | |
| 57 | it("renders the skill source icon inside its badge", () => { | |
| 33 | it("shows the app source for an app skill", () => { | |
| 58 | 34 | const markup = renderToStaticMarkup( |
| 59 | 35 | <ComposerCommandMenu |
| 60 | 36 | items={[ |
| ... | ... | @@ -85,18 +61,10 @@ describe("ComposerCommandMenu", () => { |
| 85 | 61 | expect(markup).toContain('data-slot="badge"'); |
| 86 | 62 | expect(markup).toContain(">App Skill</span>"); |
| 87 | 63 | expect(markup).toContain("Open and control the in-app browser"); |
| 88 | expect(markup).toContain("max-w-[48ch]"); | |
| 89 | expect(markup).toContain("text-secondary-label text-xs"); | |
| 90 | expect(markup).toContain("ms-auto"); | |
| 91 | expect(markup).toContain("text-current"); | |
| 92 | expect(markup.indexOf("Open and control the in-app browser")).toBeLessThan( | |
| 93 | markup.indexOf(">App Skill</span>"), | |
| 94 | ); | |
| 95 | 64 | expect(markup).toContain("<svg"); |
| 96 | expect(markup.indexOf('data-slot="badge"')).toBeLessThan(markup.indexOf("<svg")); | |
| 97 | 65 | }); |
| 98 | 66 | |
| 99 | it("keeps slash skills aligned with the source icon inside the badge", () => { | |
| 67 | it("shows the repo source for a slash skill", () => { | |
| 100 | 68 | const markup = renderToStaticMarkup( |
| 101 | 69 | <ComposerCommandMenu |
| 102 | 70 | items={[ |
| ... | ... | @@ -129,6 +97,5 @@ describe("ComposerCommandMenu", () => { |
| 129 | 97 | expect(markup).toContain("lucide-folder"); |
| 130 | 98 | expect(markup).toContain(">Repo</span>"); |
| 131 | 99 | expect(markup).toContain("Find the right skill or workflow"); |
| 132 | expect(markup).not.toContain("font-medium text-secondary-label"); | |
| 133 | 100 | }); |
| 134 | 101 | }); |
apps/web/src/components/chat/ComposerPrimaryActions.test.tsx+1-32| ... | ... | @@ -44,27 +44,6 @@ function renderPendingActions(isRunning: boolean) { |
| 44 | 44 | ); |
| 45 | 45 | } |
| 46 | 46 | |
| 47 | function renderStandaloneStop() { | |
| 48 | return renderToStaticMarkup( | |
| 49 | createElement(ComposerPrimaryActions, { | |
| 50 | compact: true, | |
| 51 | pendingAction: null, | |
| 52 | isRunning: true, | |
| 53 | showPlanFollowUpPrompt: false, | |
| 54 | promptHasText: false, | |
| 55 | isSendBusy: false, | |
| 56 | sendDisabledReason: null, | |
| 57 | isConnecting: false, | |
| 58 | isEnvironmentUnavailable: false, | |
| 59 | isPreparingWorktree: false, | |
| 60 | hasSendableContent: false, | |
| 61 | onPreviousPendingQuestion: () => {}, | |
| 62 | onInterrupt: () => {}, | |
| 63 | onImplementPlanInNewThread: () => {}, | |
| 64 | }), | |
| 65 | ); | |
| 66 | } | |
| 67 | ||
| 68 | 47 | function renderRunningActions(showSendWhileRunning: boolean, hasSendableContent: boolean) { |
| 69 | 48 | return renderToStaticMarkup( |
| 70 | 49 | createElement(ComposerPrimaryActions, { |
| ... | ... | @@ -219,12 +198,6 @@ describe("ComposerPrimaryActions", () => { |
| 219 | 198 | expect(renderPendingActions(false)).not.toContain('aria-label="Stop generation"'); |
| 220 | 199 | }); |
| 221 | 200 | |
| 222 | it("matches the small pending action size without changing the standalone size", () => { | |
| 223 | expect(renderPendingActions(true)).toContain("size-8 sm:size-7"); | |
| 224 | expect(renderStandaloneStop()).toContain("size-8 sm:h-8 sm:w-8"); | |
| 225 | expect(renderStandaloneStop()).not.toContain("sm:size-7"); | |
| 226 | }); | |
| 227 | ||
| 228 | 201 | it("renders stage artwork inside the send button when artwork identification is active", () => { |
| 229 | 202 | stageArtworkState.mode = "artwork"; |
| 230 | 203 | stageArtworkState.variant = "nightly"; |
| ... | ... | @@ -232,17 +205,14 @@ describe("ComposerPrimaryActions", () => { |
| 232 | 205 | const markup = renderSendButton(); |
| 233 | 206 | |
| 234 | 207 | expect(markup).toContain("stage-nightly"); |
| 235 | expect(markup).toContain("bg-transparent text-white"); | |
| 236 | expect(markup).not.toContain("bg-message-action text-message-action-foreground"); | |
| 237 | 208 | }); |
| 238 | 209 | |
| 239 | it("keeps the normal send-button fill when artwork identification is inactive", () => { | |
| 210 | it("hides stage artwork when artwork identification is inactive", () => { | |
| 240 | 211 | stageArtworkState.variant = "nightly"; |
| 241 | 212 | |
| 242 | 213 | const markup = renderSendButton(); |
| 243 | 214 | |
| 244 | 215 | expect(markup).not.toContain("stage-nightly"); |
| 245 | expect(markup).toContain("bg-message-action text-message-action-foreground"); | |
| 246 | 216 | }); |
| 247 | 217 | |
| 248 | 218 | it("only renders stop while running when Enter-to-send is available", () => { |
| ... | ... | @@ -258,7 +228,6 @@ describe("ComposerPrimaryActions", () => { |
| 258 | 228 | expect(markup).toContain('aria-label="Stop generation"'); |
| 259 | 229 | expect(markup).toContain('aria-label="Send message"'); |
| 260 | 230 | expect(markup).toContain('type="submit"'); |
| 261 | expect(markup).toContain("size-9 sm:size-8"); | |
| 262 | 231 | }); |
| 263 | 232 | |
| 264 | 233 | it("keeps stop as the only action while running with an empty composer", () => { |
apps/web/src/components/chat/MessagesTimeline.test.tsx+1-126| ... | ... | @@ -16,12 +16,7 @@ vi.mock("@legendapp/list/react", async () => { |
| 16 | 16 | ListFooterComponent?: ReactNode; |
| 17 | 17 | anchoredEndSpace?: { |
| 18 | 18 | anchorIndex: number; |
| 19 | anchorMaxSize?: number; | |
| 20 | anchorOffset?: number; | |
| 21 | onReady?: (info: { anchorIndex: number }) => void; | |
| 22 | 19 | }; |
| 23 | contentInsetEndAdjustment?: number; | |
| 24 | className?: string; | |
| 25 | 20 | maintainScrollAtEnd?: |
| 26 | 21 | | boolean |
| 27 | 22 | | { |
| ... | ... | @@ -32,27 +27,12 @@ vi.mock("@legendapp/list/react", async () => { |
| 32 | 27 | layout?: boolean; |
| 33 | 28 | }; |
| 34 | 29 | }; |
| 35 | maintainVisibleContentPosition?: | |
| 36 | | boolean | |
| 37 | | { | |
| 38 | data?: boolean; | |
| 39 | size?: boolean; | |
| 40 | shouldRestorePosition?: (item: { id: string }) => boolean; | |
| 41 | }; | |
| 42 | 30 | ref?: Ref<LegendListRef>; |
| 43 | 31 | }) => { |
| 44 | if (props.anchoredEndSpace) { | |
| 45 | props.anchoredEndSpace.onReady?.({ anchorIndex: props.anchoredEndSpace.anchorIndex }); | |
| 46 | } | |
| 47 | 32 | return ( |
| 48 | 33 | <div |
| 49 | 34 | data-testid={legendListTestId} |
| 50 | 35 | data-anchor-index={props.anchoredEndSpace?.anchorIndex} |
| 51 | data-anchor-max-size={props.anchoredEndSpace?.anchorMaxSize} | |
| 52 | data-anchor-offset={props.anchoredEndSpace?.anchorOffset} | |
| 53 | data-anchor-on-ready={Boolean(props.anchoredEndSpace?.onReady)} | |
| 54 | data-content-inset-end={props.contentInsetEndAdjustment} | |
| 55 | data-class-name={props.className} | |
| 56 | 36 | data-maintain-scroll-at-end={props.maintainScrollAtEnd ? "enabled" : undefined} |
| 57 | 37 | data-maintain-scroll-at-end-animated={ |
| 58 | 38 | typeof props.maintainScrollAtEnd === "object" |
| ... | ... | @@ -74,26 +54,6 @@ vi.mock("@legendapp/list/react", async () => { |
| 74 | 54 | ? props.maintainScrollAtEnd.on?.layout |
| 75 | 55 | : undefined |
| 76 | 56 | } |
| 77 | data-maintain-visible-content-position={ | |
| 78 | typeof props.maintainVisibleContentPosition === "object" | |
| 79 | ? "object" | |
| 80 | : props.maintainVisibleContentPosition | |
| 81 | } | |
| 82 | data-maintain-visible-content-position-data={ | |
| 83 | typeof props.maintainVisibleContentPosition === "object" | |
| 84 | ? props.maintainVisibleContentPosition.data | |
| 85 | : undefined | |
| 86 | } | |
| 87 | data-maintain-visible-content-position-size={ | |
| 88 | typeof props.maintainVisibleContentPosition === "object" | |
| 89 | ? props.maintainVisibleContentPosition.size | |
| 90 | : undefined | |
| 91 | } | |
| 92 | data-maintain-visible-content-position-restore={ | |
| 93 | typeof props.maintainVisibleContentPosition === "object" | |
| 94 | ? Boolean(props.maintainVisibleContentPosition.shouldRestorePosition) | |
| 95 | : undefined | |
| 96 | } | |
| 97 | 57 | > |
| 98 | 58 | {props.ListHeaderComponent} |
| 99 | 59 | {props.data.map((item) => ( |
| ... | ... | @@ -293,7 +253,7 @@ describe("MessagesTimeline", () => { |
| 293 | 253 | expect(markup).toContain("codex-thread-1"); |
| 294 | 254 | }); |
| 295 | 255 | |
| 296 | it("renders the worked-for row at assistant response text size", () => { | |
| 256 | it("renders elapsed time for a completed turn", () => { | |
| 297 | 257 | const turnId = TurnId.make("turn-with-fold"); |
| 298 | 258 | const assistantEntry = buildAssistantTimelineEntry("Done."); |
| 299 | 259 | const markup = renderToStaticMarkup( |
| ... | ... | @@ -328,23 +288,6 @@ describe("MessagesTimeline", () => { |
| 328 | 288 | ); |
| 329 | 289 | |
| 330 | 290 | expect(markup).toContain("Worked for 8.0s"); |
| 331 | expect(markup).toContain("px-1 text-sm leading-relaxed text-muted-foreground"); | |
| 332 | }); | |
| 333 | ||
| 334 | it("uses the larger leading inset only when the top fade is enabled", () => { | |
| 335 | const timelineEntries = [buildUserTimelineEntry("Hello")]; | |
| 336 | ||
| 337 | const compactMarkup = renderToStaticMarkup( | |
| 338 | <MessagesTimeline {...buildProps()} timelineEntries={timelineEntries} />, | |
| 339 | ); | |
| 340 | const fadedMarkup = renderToStaticMarkup( | |
| 341 | <MessagesTimeline {...buildProps()} timelineEntries={timelineEntries} topFadeEnabled />, | |
| 342 | ); | |
| 343 | ||
| 344 | expect(compactMarkup).toContain('class="h-3 sm:h-4"'); | |
| 345 | expect(compactMarkup).not.toContain("topbar-scroll-fade"); | |
| 346 | expect(fadedMarkup).toContain('class="h-[var(--workspace-titlebar-scroll-fade-height)]"'); | |
| 347 | expect(fadedMarkup).toContain("topbar-scroll-fade"); | |
| 348 | 291 | }); |
| 349 | 292 | |
| 350 | 293 | it("keeps assistant changed-files headers sticky below the thread header", () => { |
| ... | ... | @@ -491,74 +434,6 @@ describe("MessagesTimeline", () => { |
| 491 | 434 | expect(resolveTimelineMinimapInteractiveWidth(40, true)).toBe("22rem"); |
| 492 | 435 | }); |
| 493 | 436 | |
| 494 | it("anchors the first user message using its measured height", () => { | |
| 495 | const onAnchorReady = vi.fn(); | |
| 496 | const firstEntry = { | |
| 497 | ...buildUserTimelineEntry("First prompt."), | |
| 498 | message: { | |
| 499 | ...buildUserTimelineEntry("First prompt.").message, | |
| 500 | attachments: [ | |
| 501 | { | |
| 502 | type: "image" as const, | |
| 503 | id: "attachment-1", | |
| 504 | name: "screenshot.png", | |
| 505 | mimeType: "image/png", | |
| 506 | sizeBytes: 1, | |
| 507 | previewUrl: "data:image/png;base64,iVBORw0KGgo=", | |
| 508 | }, | |
| 509 | ], | |
| 510 | }, | |
| 511 | }; | |
| 512 | const markup = renderToStaticMarkup( | |
| 513 | <MessagesTimeline | |
| 514 | {...buildProps()} | |
| 515 | anchorMessageId={firstEntry.message.id} | |
| 516 | onAnchorReady={onAnchorReady} | |
| 517 | contentInsetEndAdjustment={144} | |
| 518 | timelineEntries={[firstEntry]} | |
| 519 | />, | |
| 520 | ); | |
| 521 | ||
| 522 | expect(markup).toContain('data-anchor-index="0"'); | |
| 523 | expect(markup).toContain('data-anchor-offset="24"'); | |
| 524 | expect(markup).toContain('data-anchor-on-ready="true"'); | |
| 525 | expect(markup).not.toContain("data-anchor-max-size="); | |
| 526 | expect(markup).toContain('data-content-inset-end="144"'); | |
| 527 | expect(markup).toContain("[overflow-anchor:none]"); | |
| 528 | expect(markup).not.toContain('data-maintain-scroll-at-end="enabled"'); | |
| 529 | expect(markup).toContain('data-maintain-visible-content-position="object"'); | |
| 530 | expect(markup).toContain('data-maintain-visible-content-position-data="true"'); | |
| 531 | expect(markup).toContain('data-maintain-visible-content-position-size="true"'); | |
| 532 | expect(markup).toContain('data-maintain-visible-content-position-restore="true"'); | |
| 533 | expect(onAnchorReady).toHaveBeenCalledOnce(); | |
| 534 | expect(onAnchorReady).toHaveBeenCalledWith(firstEntry.message.id, 0); | |
| 535 | }); | |
| 536 | ||
| 537 | it("does not reserve end space for a follow-up user message", () => { | |
| 538 | const onAnchorReady = vi.fn(); | |
| 539 | const firstEntry = buildUserTimelineEntry("First prompt."); | |
| 540 | const secondEntry = { | |
| 541 | ...buildUserTimelineEntry("Newest prompt."), | |
| 542 | id: "entry-2", | |
| 543 | message: { | |
| 544 | ...buildUserTimelineEntry("Newest prompt.").message, | |
| 545 | id: MessageId.make("message-2"), | |
| 546 | }, | |
| 547 | }; | |
| 548 | const markup = renderToStaticMarkup( | |
| 549 | <MessagesTimeline | |
| 550 | {...buildProps()} | |
| 551 | anchorMessageId={secondEntry.message.id} | |
| 552 | onAnchorReady={onAnchorReady} | |
| 553 | timelineEntries={[firstEntry, secondEntry]} | |
| 554 | />, | |
| 555 | ); | |
| 556 | ||
| 557 | expect(markup).not.toContain("data-anchor-index="); | |
| 558 | expect(markup).toContain('data-maintain-scroll-at-end="enabled"'); | |
| 559 | expect(onAnchorReady).not.toHaveBeenCalled(); | |
| 560 | }); | |
| 561 | ||
| 562 | 437 | it("renders generic attachments as download links instead of image previews", () => { |
| 563 | 438 | const entry = { |
| 564 | 439 | ...buildUserTimelineEntry("Read the report."), |
apps/web/src/components/chat/providerIconUtils.ts-12| ... | ... | @@ -1,6 +1,5 @@ |
| 1 | 1 | import { ProviderDriverKind } from "@t3tools/contracts"; |
| 2 | 2 | import { ClaudeAI, CursorIcon, GrokIcon, Icon, OpenAI, OpenCodeIcon } from "../Icons"; |
| 3 | import { PROVIDER_OPTIONS } from "../../session-logic"; | |
| 4 | 3 | |
| 5 | 4 | export const PROVIDER_ICON_BY_PROVIDER: Partial<Record<ProviderDriverKind, Icon>> = { |
| 6 | 5 | [ProviderDriverKind.make("codex")]: OpenAI, |
| ... | ... | @@ -10,17 +9,6 @@ export const PROVIDER_ICON_BY_PROVIDER: Partial<Record<ProviderDriverKind, Icon> |
| 10 | 9 | [ProviderDriverKind.make("grok")]: GrokIcon, |
| 11 | 10 | }; |
| 12 | 11 | |
| 13 | function isAvailableProviderOption(option: (typeof PROVIDER_OPTIONS)[number]): option is { | |
| 14 | value: ProviderDriverKind; | |
| 15 | label: string; | |
| 16 | available: true; | |
| 17 | pickerSidebarBadge?: "new" | "soon"; | |
| 18 | } { | |
| 19 | return option.available; | |
| 20 | } | |
| 21 | ||
| 22 | export const AVAILABLE_PROVIDER_OPTIONS = PROVIDER_OPTIONS.filter(isAvailableProviderOption); | |
| 23 | ||
| 24 | 12 | export type ModelEsque = { |
| 25 | 13 | slug: string; |
| 26 | 14 | name: string; |
apps/web/src/components/desktopUpdate.logic.ts-5| ... | ... | @@ -125,11 +125,6 @@ export function shouldToastDesktopUpdateActionResult(result: DesktopUpdateAction |
| 125 | 125 | return getDesktopUpdateActionError(result) !== null; |
| 126 | 126 | } |
| 127 | 127 | |
| 128 | export function shouldHighlightDesktopUpdateError(state: DesktopUpdateState | null): boolean { | |
| 129 | if (!state || state.status !== "error") return false; | |
| 130 | return state.errorContext === "download" || state.errorContext === "install"; | |
| 131 | } | |
| 132 | ||
| 133 | 128 | export function canCheckForUpdate(state: DesktopUpdateState | null): boolean { |
| 134 | 129 | if (!state || !state.enabled) return false; |
| 135 | 130 | return ( |
apps/web/src/composer-logic.ts-2| ... | ... | @@ -221,8 +221,6 @@ export function isCollapsedCursorAdjacentToInlineToken( |
| 221 | 221 | return false; |
| 222 | 222 | } |
| 223 | 223 | |
| 224 | export const isCollapsedCursorAdjacentToMention = isCollapsedCursorAdjacentToInlineToken; | |
| 225 | ||
| 226 | 224 | export function detectComposerTrigger(text: string, cursorInput: number): ComposerTrigger | null { |
| 227 | 225 | const cursor = clampCursor(text, cursorInput); |
| 228 | 226 | const lineStart = text.lastIndexOf("\n", Math.max(0, cursor - 1)) + 1; |
apps/web/src/composerDraftStore.ts-8| ... | ... | @@ -4117,11 +4117,3 @@ export function finalizePromotedDraftThreadByRef(threadRef: ScopedThreadRef): vo |
| 4117 | 4117 | } |
| 4118 | 4118 | clearBackgroundDraftSubmissionByRef(threadRef); |
| 4119 | 4119 | } |
| 4120 | ||
| 4121 | export function finalizePromotedDraftThreadsByRef( | |
| 4122 | serverThreadRefs: Iterable<ScopedThreadRef>, | |
| 4123 | ): void { | |
| 4124 | for (const threadRef of serverThreadRefs) { | |
| 4125 | finalizePromotedDraftThreadByRef(threadRef); | |
| 4126 | } | |
| 4127 | } |
apps/web/src/keybindings.ts-16| ... | ... | @@ -411,14 +411,6 @@ export function isDiffToggleShortcut( |
| 411 | 411 | return matchesCommandShortcut(event, keybindings, "diff.toggle", options); |
| 412 | 412 | } |
| 413 | 413 | |
| 414 | export function isPreviewToggleShortcut( | |
| 415 | event: ShortcutEventLike, | |
| 416 | keybindings: ResolvedKeybindingsConfig, | |
| 417 | options?: ShortcutMatchOptions, | |
| 418 | ): boolean { | |
| 419 | return matchesCommandShortcut(event, keybindings, "preview.toggle", options); | |
| 420 | } | |
| 421 | ||
| 422 | 414 | export function isPreviewRefreshShortcut( |
| 423 | 415 | event: ShortcutEventLike, |
| 424 | 416 | keybindings: ResolvedKeybindingsConfig, |
| ... | ... | @@ -427,14 +419,6 @@ export function isPreviewRefreshShortcut( |
| 427 | 419 | return matchesCommandShortcut(event, keybindings, "preview.refresh", options); |
| 428 | 420 | } |
| 429 | 421 | |
| 430 | export function isPreviewFocusUrlShortcut( | |
| 431 | event: ShortcutEventLike, | |
| 432 | keybindings: ResolvedKeybindingsConfig, | |
| 433 | options?: ShortcutMatchOptions, | |
| 434 | ): boolean { | |
| 435 | return matchesCommandShortcut(event, keybindings, "preview.focusUrl", options); | |
| 436 | } | |
| 437 | ||
| 438 | 422 | export function isChatNewShortcut( |
| 439 | 423 | event: ShortcutEventLike, |
| 440 | 424 | keybindings: ResolvedKeybindingsConfig, |
apps/web/src/lib/contextWindow.ts-22| ... | ... | @@ -25,28 +25,6 @@ export type ContextWindowSnapshot = NullableContextWindowUsage & { |
| 25 | 25 | readonly updatedAt: string; |
| 26 | 26 | }; |
| 27 | 27 | |
| 28 | /** Map a provider driver kind to a user-facing display name. */ | |
| 29 | export function formatProviderDisplayName(provider: string | null | undefined): string { | |
| 30 | if (!provider) return "This agent"; | |
| 31 | switch (provider) { | |
| 32 | case "claudeAgent": | |
| 33 | case "claude": | |
| 34 | return "Claude"; | |
| 35 | case "codex": | |
| 36 | return "Codex"; | |
| 37 | case "cursor": | |
| 38 | return "Cursor"; | |
| 39 | case "opencode": | |
| 40 | return "OpenCode"; | |
| 41 | default: { | |
| 42 | // Title-case unknown driver kinds so they read reasonably. | |
| 43 | const trimmed = provider.replace(/Agent$/i, "").trim(); | |
| 44 | if (trimmed.length === 0) return provider; | |
| 45 | return trimmed.charAt(0).toUpperCase() + trimmed.slice(1); | |
| 46 | } | |
| 47 | } | |
| 48 | } | |
| 49 | ||
| 50 | 28 | export function deriveLatestContextWindowSnapshot( |
| 51 | 29 | activities: ReadonlyArray<OrchestrationThreadActivity>, |
| 52 | 30 | ): ContextWindowSnapshot | null { |
apps/web/src/lib/utils.ts+1-7| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | import { CommandId, MessageId, ProjectId, ThreadId } from "@t3tools/contracts"; | |
| 1 | import { MessageId, ProjectId, ThreadId } from "@t3tools/contracts"; | |
| 2 | 2 | import { type CxOptions, cx } from "class-variance-authority"; |
| 3 | 3 | import * as Encoding from "effect/Encoding"; |
| 4 | 4 | import { twMerge } from "tailwind-merge"; |
| ... | ... | @@ -16,10 +16,6 @@ export function isWindowsPlatform(platform: string): boolean { |
| 16 | 16 | return /^win(dows)?/i.test(platform); |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | export function isLinuxPlatform(platform: string): boolean { | |
| 20 | return /linux/i.test(platform); | |
| 21 | } | |
| 22 | ||
| 23 | 19 | export function normalizeSearchText(value: string): string { |
| 24 | 20 | return value.normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase().replace(/\s+/g, " ").trim(); |
| 25 | 21 | } |
| ... | ... | @@ -46,8 +42,6 @@ export function randomUUID(): string { |
| 46 | 42 | return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`; |
| 47 | 43 | } |
| 48 | 44 | |
| 49 | export const newCommandId = (): CommandId => CommandId.make(randomUUID()); | |
| 50 | ||
| 51 | 45 | export const newProjectId = (): ProjectId => ProjectId.make(randomUUID()); |
| 52 | 46 | |
| 53 | 47 | export const newThreadId = (): ThreadId => ThreadId.make(randomUUID()); |
apps/web/src/portDiscoveryState.ts-19| ... | ... | @@ -85,22 +85,3 @@ export function useThreadDiscoveredPorts(input: { |
| 85 | 85 | [input.threadId, ports], |
| 86 | 86 | ); |
| 87 | 87 | } |
| 88 | ||
| 89 | export function useTerminalDiscoveredPorts(input: { | |
| 90 | readonly environmentId: EnvironmentId | null; | |
| 91 | readonly threadId: ThreadId | null; | |
| 92 | readonly terminalId: string | null; | |
| 93 | }): ReadonlyArray<DiscoveredLocalServer> { | |
| 94 | const ports = useDiscoveredPorts(input.environmentId); | |
| 95 | return useMemo( | |
| 96 | () => | |
| 97 | input.threadId && input.terminalId | |
| 98 | ? ports.filter( | |
| 99 | (port) => | |
| 100 | port.terminal?.threadId === input.threadId && | |
| 101 | port.terminal.terminalId === input.terminalId, | |
| 102 | ) | |
| 103 | : EMPTY_PORTS, | |
| 104 | [input.terminalId, input.threadId, ports], | |
| 105 | ); | |
| 106 | } |
apps/web/src/providerInstances.ts-11| ... | ... | @@ -299,17 +299,6 @@ export function getProviderInstanceEntry( |
| 299 | 299 | return deriveProviderInstanceEntries(providers).find((entry) => entry.instanceId === instanceId); |
| 300 | 300 | } |
| 301 | 301 | |
| 302 | /** | |
| 303 | * Model list for a specific instance. Returns `[]` when the instance isn't | |
| 304 | * present so callers don't have to thread optionality through render code. | |
| 305 | */ | |
| 306 | export function getProviderInstanceModels( | |
| 307 | providers: ReadonlyArray<ServerProvider>, | |
| 308 | instanceId: ProviderInstanceId, | |
| 309 | ): ReadonlyArray<ServerProviderModel> { | |
| 310 | return getProviderInstanceEntry(providers, instanceId)?.models ?? []; | |
| 311 | } | |
| 312 | ||
| 313 | 302 | /** |
| 314 | 303 | * Default model slug for a specific instance: its declared built-in default, |
| 315 | 304 | * then its first built-in model, then any model it reports, then the driver-level default. Custom |
apps/web/src/providerModels.ts-18| ... | ... | @@ -38,14 +38,6 @@ export function getProviderSnapshot( |
| 38 | 38 | return providers.find((candidate) => candidate.instanceId === defaultInstanceId); |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | export function getProviderDisplayName( | |
| 42 | providers: ReadonlyArray<ServerProvider>, | |
| 43 | provider: ProviderDriverKind, | |
| 44 | ): string { | |
| 45 | const snapshot = getProviderSnapshot(providers, provider); | |
| 46 | return snapshot?.displayName?.trim() || formatProviderDriverKindLabel(provider); | |
| 47 | } | |
| 48 | ||
| 49 | 41 | export function getProviderInteractionModeToggle( |
| 50 | 42 | providers: ReadonlyArray<ServerProvider>, |
| 51 | 43 | provider: ProviderDriverKind, |
| ... | ... | @@ -53,16 +45,6 @@ export function getProviderInteractionModeToggle( |
| 53 | 45 | return getProviderSnapshot(providers, provider)?.showInteractionModeToggle ?? true; |
| 54 | 46 | } |
| 55 | 47 | |
| 56 | export function isProviderEnabled( | |
| 57 | providers: ReadonlyArray<ServerProvider>, | |
| 58 | provider: ProviderDriverKind, | |
| 59 | ): boolean { | |
| 60 | if (providers.length === 0) { | |
| 61 | return true; | |
| 62 | } | |
| 63 | return getProviderSnapshot(providers, provider)?.enabled ?? false; | |
| 64 | } | |
| 65 | ||
| 66 | 48 | // Resolve an instance selection to the correlated live driver. If the |
| 67 | 49 | // instance is absent, fall back to a live enabled provider instead of |
| 68 | 50 | // inferring a driver from the missing instance id. |
apps/web/src/routes/-chatIndexTitlebar.test.ts deleted-29| ... | ... | @@ -1,29 +0,0 @@ |
| 1 | // @effect-diagnostics nodeBuiltinImport:off | |
| 2 | // Regression coverage compares the onboarding header with the shared titlebar contract. | |
| 3 | import * as NodeFS from "node:fs"; | |
| 4 | ||
| 5 | import { describe, expect, it } from "vite-plus/test"; | |
| 6 | ||
| 7 | describe("hosted static onboarding header", () => { | |
| 8 | it("uses the shared workspace topbar geometry", () => { | |
| 9 | const routeSource = NodeFS.readFileSync(new URL("./_chat.index.tsx", import.meta.url), "utf8"); | |
| 10 | const onboardingStart = routeSource.indexOf("function HostedStaticOnboardingState()"); | |
| 11 | const onboardingEnd = routeSource.indexOf('<Empty className="flex-1">', onboardingStart); | |
| 12 | ||
| 13 | expect(onboardingStart).toBeGreaterThanOrEqual(0); | |
| 14 | expect(onboardingEnd).toBeGreaterThan(onboardingStart); | |
| 15 | ||
| 16 | const onboardingHeader = routeSource.slice(onboardingStart, onboardingEnd); | |
| 17 | ||
| 18 | expect(onboardingHeader).toContain("<WorkspacePageHeader"); | |
| 19 | expect(onboardingHeader).not.toMatch(/(?:^|\s)(?:[\w-]+:)*py-/); | |
| 20 | ||
| 21 | const headerSource = NodeFS.readFileSync( | |
| 22 | new URL("../components/WorkspacePageHeader.tsx", import.meta.url), | |
| 23 | "utf8", | |
| 24 | ); | |
| 25 | expect(headerSource).toContain("h-[var(--workspace-topbar-height)]"); | |
| 26 | expect(headerSource).toContain("min-h-[var(--workspace-topbar-height)]"); | |
| 27 | expect(headerSource).toContain("COLLAPSED_SIDEBAR_TITLEBAR_INSET_CLASS"); | |
| 28 | }); | |
| 29 | }); |
apps/web/src/themePalette.ts-2| ... | ... | @@ -1521,8 +1521,6 @@ export class ThemeLibraryStorageError extends Schema.TaggedErrorClass<ThemeLibra |
| 1521 | 1521 | } |
| 1522 | 1522 | } |
| 1523 | 1523 | |
| 1524 | export const isThemeLibraryStorageError = Schema.is(ThemeLibraryStorageError); | |
| 1525 | ||
| 1526 | 1524 | function saveCustomThemes( |
| 1527 | 1525 | storedThemes: ReadonlyArray<unknown>, |
| 1528 | 1526 | themes: ReadonlyArray<ThemeDefinition>, |
pnpm-lock.yaml-6| ... | ... | @@ -232,9 +232,6 @@ importers: |
| 232 | 232 | '@noble/curves': |
| 233 | 233 | specifier: 'catalog:' |
| 234 | 234 | version: 1.9.1 |
| 235 | '@noble/hashes': | |
| 236 | specifier: 'catalog:' | |
| 237 | version: 1.8.0 | |
| 238 | 235 | '@pierre/diffs': |
| 239 | 236 | specifier: 'catalog:' |
| 240 | 237 | version: 1.3.0-beta.10(patch_hash=7ef7cb0cbabb17c15cdb137554068b36f15f6f5265e73fb51452aa3380db91aa)(@shikijs/themes@4.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) |
| ... | ... | @@ -479,9 +476,6 @@ importers: |
| 479 | 476 | '@effect/platform-node': |
| 480 | 477 | specifier: 4.0.0-beta.103 |
| 481 | 478 | version: 4.0.0-beta.103(bufferutil@4.1.0)(effect@4.0.0-beta.103(patch_hash=af36b7948b6f9c56623074662b51dade5699880c1a7c71245de73e13c3185fb6))(ioredis@5.11.0)(utf-8-validate@6.0.6) |
| 482 | '@effect/platform-node-shared': | |
| 483 | specifier: 4.0.0-beta.103 | |
| 484 | version: 4.0.0-beta.103(bufferutil@4.1.0)(effect@4.0.0-beta.103(patch_hash=af36b7948b6f9c56623074662b51dade5699880c1a7c71245de73e13c3185fb6))(utf-8-validate@6.0.6) | |
| 485 | 479 | '@effect/sql-sqlite-bun': |
| 486 | 480 | specifier: 4.0.0-beta.103 |
| 487 | 481 | version: 4.0.0-beta.103(effect@4.0.0-beta.103(patch_hash=af36b7948b6f9c56623074662b51dade5699880c1a7c71245de73e13c3185fb6)) |
vite.config.ts-1| ... | ... | @@ -37,7 +37,6 @@ export default defineConfig({ |
| 37 | 37 | "**/routeTree.gen.ts", |
| 38 | 38 | "apps/mobile/android/**", |
| 39 | 39 | "apps/mobile/ios/**", |
| 40 | "apps/web/public/mockServiceWorker.js", | |
| 41 | 40 | "apps/web/src/lib/vendor/qrcodegen.ts", |
| 42 | 41 | "apps/mobile/uniwind-types.d.ts", |
| 43 | 42 | "*.icon/**", |