Skip to content

Commit 557faca

Browse files
authored
Merge branch 'main' into fix/show-more-cursor-and-transition
2 parents 62553cd + f6978db commit 557faca

216 files changed

Lines changed: 74312 additions & 7181 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.devcontainer/devcontainer.json

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,22 @@
22
"name": "T3 Code Dev",
33
"image": "debian:bookworm",
44
"features": {
5-
"ghcr.io/devcontainers-extra/features/bun:1": {},
5+
"ghcr.io/devcontainers/features/git:1": {},
6+
"ghcr.io/devcontainers-extra/features/bun:1": {
7+
"version": "1.3.11"
8+
},
69
"ghcr.io/devcontainers/features/node:1": {
7-
"version": "24",
8-
"nodeGypDependencies": true
10+
"version": "24.13.1"
911
},
1012
"ghcr.io/devcontainers/features/python:1": {
11-
"version": "3.12"
13+
"version": "3.10",
14+
"installTools": false
1215
}
1316
},
17+
"overrideFeatureInstallOrder": [
18+
"ghcr.io/devcontainers/features/git",
19+
"ghcr.io/devcontainers-extra/features/bun"
20+
],
1421
"postCreateCommand": {
1522
"bun-install": "bun install --backend=copyfile --frozen-lockfile"
1623
},

.github/workflows/release.yml

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ on:
44
push:
55
tags:
66
- "v*.*.*"
7+
- "!v*-nightly.*"
78
schedule:
8-
- cron: "0 9 * * *"
9+
- cron: "0 */3 * * *"
910
workflow_dispatch:
1011
inputs:
1112
channel:
@@ -26,8 +27,45 @@ permissions:
2627
id-token: write
2728

2829
jobs:
30+
check_changes:
31+
name: Check for changes since last nightly
32+
if: github.event_name == 'schedule'
33+
runs-on: ubuntu-24.04
34+
outputs:
35+
has_changes: ${{ steps.check.outputs.has_changes }}
36+
steps:
37+
- name: Checkout
38+
uses: actions/checkout@v6
39+
with:
40+
fetch-depth: 0
41+
42+
- id: check
43+
name: Compare HEAD to last nightly tag
44+
run: |
45+
last_nightly_tag=$(git tag --list 'v*-nightly.*' 'nightly-v*' --sort=-creatordate | head -n 1)
46+
if [[ -z "$last_nightly_tag" ]]; then
47+
echo "No previous nightly tag found. Proceeding with release."
48+
echo "has_changes=true" >> "$GITHUB_OUTPUT"
49+
exit 0
50+
fi
51+
52+
last_nightly_sha=$(git rev-parse "$last_nightly_tag^{commit}")
53+
head_sha=$(git rev-parse HEAD)
54+
55+
if [[ "$last_nightly_sha" == "$head_sha" ]]; then
56+
echo "No changes on main since last nightly release ($last_nightly_tag). Skipping."
57+
echo "has_changes=false" >> "$GITHUB_OUTPUT"
58+
else
59+
echo "Changes detected on main since $last_nightly_tag ($last_nightly_sha → $head_sha). Proceeding."
60+
echo "has_changes=true" >> "$GITHUB_OUTPUT"
61+
fi
62+
2963
preflight:
3064
name: Preflight
65+
needs: [check_changes]
66+
if: |
67+
!failure() && !cancelled() &&
68+
(github.event_name != 'schedule' || needs.check_changes.outputs.has_changes == 'true')
3169
runs-on: blacksmith-8vcpu-ubuntu-2404
3270
timeout-minutes: 10
3371
outputs:
@@ -134,6 +172,7 @@ jobs:
134172
build:
135173
name: Build ${{ matrix.label }}
136174
needs: preflight
175+
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' }}
137176
runs-on: ${{ matrix.runner }}
138177
timeout-minutes: 30
139178
strategy:
@@ -156,7 +195,7 @@ jobs:
156195
target: AppImage
157196
arch: x64
158197
- label: Windows x64
159-
runner: windows-2022 # blacksmith-32vcpu-windows-2025
198+
runner: blacksmith-32vcpu-windows-2025
160199
platform: win
161200
target: nsis
162201
arch: x64
@@ -188,6 +227,23 @@ jobs:
188227
- name: Align package versions to release version
189228
run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}"
190229

230+
- name: Install Spectre-mitigated MSVC libs
231+
if: matrix.platform == 'win'
232+
shell: pwsh
233+
run: |
234+
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
235+
$installPath = & $vswhere -products * -latest -property installationPath
236+
$setupExe = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\setup.exe"
237+
$proc = Start-Process -FilePath $setupExe `
238+
-ArgumentList "modify", "--installPath", "`"$installPath`"", "--add", `
239+
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64.Spectre", "--quiet", "--norestart" `
240+
-Wait -PassThru -NoNewWindow
241+
if ($null -eq $proc -or $proc.ExitCode -ne 0) {
242+
$code = if ($null -ne $proc) { $proc.ExitCode } else { 1 }
243+
Write-Error "Visual Studio Installer failed with exit code $code"
244+
exit $code
245+
}
246+
191247
- name: Build desktop artifact
192248
shell: bash
193249
env:
@@ -299,6 +355,7 @@ jobs:
299355
publish_cli:
300356
name: Publish CLI to npm
301357
needs: [preflight, build]
358+
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.build.result == 'success' }}
302359
runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404
303360
timeout-minutes: 10
304361
steps:
@@ -333,19 +390,36 @@ jobs:
333390
release:
334391
name: Publish GitHub Release
335392
needs: [preflight, build, publish_cli]
336-
runs-on: blacksmith-8vcpu-ubuntu-2404
393+
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.build.result == 'success' && needs.publish_cli.result == 'success' }}
394+
runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404
337395
timeout-minutes: 10
338396
steps:
397+
- id: app_token
398+
name: Mint release app token
399+
uses: actions/create-github-app-token@v2
400+
with:
401+
app-id: ${{ secrets.RELEASE_APP_ID }}
402+
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
403+
owner: ${{ github.repository_owner }}
404+
339405
- name: Checkout
340406
uses: actions/checkout@v6
341407
with:
342408
ref: ${{ needs.preflight.outputs.ref }}
343409

410+
- name: Setup Bun
411+
uses: oven-sh/setup-bun@v2
412+
with:
413+
bun-version-file: package.json
414+
344415
- name: Setup Node
345416
uses: actions/setup-node@v6
346417
with:
347418
node-version-file: package.json
348419

420+
- name: Install dependencies
421+
run: bun install --frozen-lockfile
422+
349423
- name: Download all desktop artifacts
350424
uses: actions/download-artifact@v8
351425
with:
@@ -412,6 +486,7 @@ jobs:
412486
release-assets/*.blockmap
413487
release-assets/*.yml
414488
fail_on_unmatched_files: true
489+
token: ${{ steps.app_token.outputs.token }}
415490

416491
- name: Publish first release
417492
if: needs.preflight.outputs.previous_tag == ''
@@ -431,10 +506,11 @@ jobs:
431506
release-assets/*.blockmap
432507
release-assets/*.yml
433508
fail_on_unmatched_files: true
509+
token: ${{ steps.app_token.outputs.token }}
434510

435511
finalize:
436512
name: Finalize release
437-
if: needs.preflight.outputs.release_channel == 'stable'
513+
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.release.result == 'success' && needs.preflight.outputs.release_channel == 'stable' }}
438514
needs: [preflight, release]
439515
runs-on: blacksmith-8vcpu-ubuntu-2404
440516
timeout-minutes: 10

.oxfmtrc.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,13 @@
1313
"apps/web/src/lib/vendor/qrcodegen.ts",
1414
"*.icon/**"
1515
],
16-
"sortPackageJson": {}
16+
"sortPackageJson": {},
17+
"overrides": [
18+
{
19+
"files": [".devcontainer/devcontainer.json"],
20+
"options": {
21+
"trailingComma": "none"
22+
}
23+
}
24+
]
1725
}

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
"source.fixAll.oxc": "always"
66
},
77
"oxc.unusedDisableDirectives": "warn",
8-
"typescript.tsdk": "node_modules/typescript/lib"
8+
"js/ts.tsdk.path": "node_modules/typescript/lib"
99
}

apps/desktop/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@t3tools/contracts": "workspace:*",
2424
"@t3tools/shared": "workspace:*",
2525
"@types/node": "catalog:",
26+
"effect-acp": "workspace:*",
2627
"tsdown": "catalog:",
2728
"typescript": "catalog:",
2829
"vitest": "catalog:"
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { describe, expect, it, vi } from "vitest";
2+
3+
import { BackendReadinessAbortedError } from "./backendReadiness.ts";
4+
import { waitForBackendStartupReady } from "./backendStartupReadiness.ts";
5+
6+
describe("waitForBackendStartupReady", () => {
7+
it("falls back to the HTTP probe when no listening signal exists", async () => {
8+
const waitForHttpReady = vi.fn<() => Promise<void>>().mockResolvedValue(undefined);
9+
const cancelHttpWait = vi.fn();
10+
11+
await expect(
12+
waitForBackendStartupReady({
13+
waitForHttpReady,
14+
cancelHttpWait,
15+
}),
16+
).resolves.toBe("http");
17+
18+
expect(waitForHttpReady).toHaveBeenCalledTimes(1);
19+
expect(cancelHttpWait).not.toHaveBeenCalled();
20+
});
21+
22+
it("uses the listening signal and cancels the HTTP probe", async () => {
23+
let rejectHttpWait: ((error: unknown) => void) | null = null;
24+
const waitForHttpReady = vi.fn(
25+
() =>
26+
new Promise<void>((_resolve, reject) => {
27+
rejectHttpWait = reject;
28+
}),
29+
);
30+
const cancelHttpWait = vi.fn(() => {
31+
rejectHttpWait?.(new BackendReadinessAbortedError());
32+
});
33+
34+
await expect(
35+
waitForBackendStartupReady({
36+
listeningPromise: Promise.resolve(),
37+
waitForHttpReady,
38+
cancelHttpWait,
39+
}),
40+
).resolves.toBe("listening");
41+
42+
expect(waitForHttpReady).toHaveBeenCalledTimes(1);
43+
expect(cancelHttpWait).toHaveBeenCalledTimes(1);
44+
});
45+
46+
it("rejects when the listening signal fails before HTTP readiness", async () => {
47+
const error = new Error("backend exited");
48+
const waitForHttpReady = vi.fn(() => new Promise<void>(() => {}));
49+
50+
await expect(
51+
waitForBackendStartupReady({
52+
listeningPromise: Promise.reject(error),
53+
waitForHttpReady,
54+
cancelHttpWait: vi.fn(),
55+
}),
56+
).rejects.toBe(error);
57+
});
58+
});
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { isBackendReadinessAborted } from "./backendReadiness.ts";
2+
3+
export interface WaitForBackendStartupReadyOptions {
4+
readonly listeningPromise?: Promise<void> | null;
5+
readonly waitForHttpReady: () => Promise<void>;
6+
readonly cancelHttpWait: () => void;
7+
}
8+
9+
export async function waitForBackendStartupReady(
10+
options: WaitForBackendStartupReadyOptions,
11+
): Promise<"listening" | "http"> {
12+
const httpReadyPromise = options.waitForHttpReady();
13+
const listeningPromise = options.listeningPromise;
14+
15+
if (!listeningPromise) {
16+
await httpReadyPromise;
17+
return "http";
18+
}
19+
20+
return await new Promise<"listening" | "http">((resolve, reject) => {
21+
let settled = false;
22+
23+
const settleResolve = (source: "listening" | "http") => {
24+
if (settled) {
25+
return;
26+
}
27+
settled = true;
28+
if (source === "listening") {
29+
options.cancelHttpWait();
30+
}
31+
resolve(source);
32+
};
33+
34+
const settleReject = (error: unknown) => {
35+
if (settled) {
36+
return;
37+
}
38+
settled = true;
39+
reject(error);
40+
};
41+
42+
listeningPromise.then(
43+
() => settleResolve("listening"),
44+
(error) => settleReject(error),
45+
);
46+
httpReadyPromise.then(
47+
() => settleResolve("http"),
48+
(error) => {
49+
if (settled && isBackendReadinessAborted(error)) {
50+
return;
51+
}
52+
settleReject(error);
53+
},
54+
);
55+
});
56+
}

apps/desktop/src/clientPersistence.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const clientSettings: ClientSettings = {
5252
confirmThreadArchive: true,
5353
confirmThreadDelete: false,
5454
diffWordWrap: true,
55+
favorites: [],
5556
sidebarProjectGroupingMode: "repository_path",
5657
sidebarProjectGroupingOverrides: {
5758
"environment-1:/tmp/project-a": "separate",

0 commit comments

Comments
 (0)