@cueloop/client 0.1.0-alpha.31 → 0.1.0-alpha.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/src/App.autoclose.test.tsx +4 -3
- package/src/App.diff.test.tsx +4 -1
- package/src/App.export.test.tsx +4 -6
- package/src/App.plan-review.test.tsx +8 -12
- package/src/App.readonly.test.tsx +4 -1
- package/src/App.rendering.test.tsx +4 -1
- package/src/App.review-panel.test.tsx +4 -3
- package/src/App.submit-confirm.test.tsx +4 -1
- package/src/App.test.tsx +4 -1
- package/src/App.tsx +1 -1
- package/src/App.walk.test.tsx +6 -3
- package/src/components/CompletionOverlay.tsx +1 -1
- package/src/components/ConfirmDialog.stories.tsx +1 -1
- package/src/components/DiffSheet.stories.tsx +3 -3
- package/src/components/DiffSheet.tsx +7 -7
- package/src/components/InboxList.stories.tsx +1 -1
- package/src/components/InboxList.tsx +2 -2
- package/src/components/PlanSheet.stories.tsx +1 -1
- package/src/components/PlanSheet.tsx +6 -6
- package/src/components/PromptDialog.stories.tsx +2 -2
- package/src/components/ReviewPanel.stories.tsx +1 -1
- package/src/components/SettingsDialog.stories.tsx +1 -1
- package/src/components/WalkWizard.stories.tsx +1 -1
- package/src/components/WalkWizard.tsx +1 -1
- package/src/components/primitives/Dialog.stories.tsx +1 -1
- package/src/components/primitives/Dialog.tsx +3 -4
- package/src/components/stories-app.tsx +1 -1
- package/src/components/stories.test.tsx +2 -0
- package/src/test-support.test.ts +55 -0
- package/src/test-support.ts +26 -0
- package/src/theme.ts +16 -16
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cueloop/client",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.32",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./src/index.ts"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@cueloop/daemon": "0.1.0-alpha.
|
|
11
|
-
"@cueloop/extension-api": "0.1.0-alpha.
|
|
12
|
-
"@cueloop/integration-obsidian": "0.1.0-alpha.
|
|
13
|
-
"@cueloop/schema": "0.1.0-alpha.
|
|
10
|
+
"@cueloop/daemon": "0.1.0-alpha.32",
|
|
11
|
+
"@cueloop/extension-api": "0.1.0-alpha.32",
|
|
12
|
+
"@cueloop/integration-obsidian": "0.1.0-alpha.32",
|
|
13
|
+
"@cueloop/schema": "0.1.0-alpha.32",
|
|
14
14
|
"@opentui/core": "^0.5.2",
|
|
15
15
|
"@opentui/keymap": "0.5.2",
|
|
16
16
|
"@opentui/react": "^0.5.2",
|
|
@@ -10,7 +10,7 @@ import { ManualClock } from "@opentui/core/testing";
|
|
|
10
10
|
import { DaemonServer } from "@cueloop/daemon";
|
|
11
11
|
import type { ReviewSession } from "@cueloop/schema";
|
|
12
12
|
import { App } from "./App";
|
|
13
|
-
import { press, settle, waitForText } from "./test-support";
|
|
13
|
+
import { isolateUserConfig, press, settle, waitForText } from "./test-support";
|
|
14
14
|
|
|
15
15
|
const PLAN = "# Plan\n\nShip the thing.\n";
|
|
16
16
|
|
|
@@ -18,12 +18,13 @@ let home: string;
|
|
|
18
18
|
let server: DaemonServer;
|
|
19
19
|
let session: ReviewSession;
|
|
20
20
|
let configPath: string;
|
|
21
|
+
let restoreUserConfig: () => void;
|
|
21
22
|
let clock: ManualClock;
|
|
22
23
|
|
|
23
24
|
beforeEach(() => {
|
|
24
25
|
home = mkdtempSync(join(tmpdir(), "cueloop-close-"));
|
|
25
26
|
configPath = join(home, "config.toml");
|
|
26
|
-
|
|
27
|
+
restoreUserConfig = isolateUserConfig(home, "config.toml");
|
|
27
28
|
server = new DaemonServer({ home, idleExitMs: 0 });
|
|
28
29
|
server.start();
|
|
29
30
|
session = server.core.sessionCreate({
|
|
@@ -33,7 +34,7 @@ beforeEach(() => {
|
|
|
33
34
|
clock = new ManualClock();
|
|
34
35
|
});
|
|
35
36
|
afterEach(() => {
|
|
36
|
-
|
|
37
|
+
restoreUserConfig();
|
|
37
38
|
server.stop();
|
|
38
39
|
rmSync(home, { recursive: true, force: true });
|
|
39
40
|
});
|
package/src/App.diff.test.tsx
CHANGED
|
@@ -9,7 +9,7 @@ import { testRender } from "@opentui/react/test-utils";
|
|
|
9
9
|
import { DaemonServer } from "@cueloop/daemon";
|
|
10
10
|
import type { ReviewSession } from "@cueloop/schema";
|
|
11
11
|
import { App } from "./App";
|
|
12
|
-
import { press, waitForText } from "./test-support";
|
|
12
|
+
import { isolateUserConfig, press, waitForText } from "./test-support";
|
|
13
13
|
|
|
14
14
|
const PATCH = `diff --git a/src/store.ts b/src/store.ts
|
|
15
15
|
index 111..222 100644
|
|
@@ -23,11 +23,13 @@ index 111..222 100644
|
|
|
23
23
|
`;
|
|
24
24
|
|
|
25
25
|
let home: string;
|
|
26
|
+
let restoreUserConfig: () => void;
|
|
26
27
|
let server: DaemonServer;
|
|
27
28
|
let session: ReviewSession;
|
|
28
29
|
|
|
29
30
|
beforeEach(() => {
|
|
30
31
|
home = mkdtempSync(join(tmpdir(), "cueloop-diff-"));
|
|
32
|
+
restoreUserConfig = isolateUserConfig(home);
|
|
31
33
|
server = new DaemonServer({ home, idleExitMs: 0 });
|
|
32
34
|
server.start();
|
|
33
35
|
session = server.core.sessionCreate({
|
|
@@ -36,6 +38,7 @@ beforeEach(() => {
|
|
|
36
38
|
});
|
|
37
39
|
});
|
|
38
40
|
afterEach(() => {
|
|
41
|
+
restoreUserConfig();
|
|
39
42
|
server.stop();
|
|
40
43
|
rmSync(home, { recursive: true, force: true });
|
|
41
44
|
});
|
package/src/App.export.test.tsx
CHANGED
|
@@ -9,7 +9,7 @@ import { testRender } from "@opentui/react/test-utils";
|
|
|
9
9
|
import { DaemonServer } from "@cueloop/daemon";
|
|
10
10
|
import type { ReviewSession } from "@cueloop/schema";
|
|
11
11
|
import { App } from "./App";
|
|
12
|
-
import { press, waitForState, waitForText } from "./test-support";
|
|
12
|
+
import { isolateUserConfig, press, waitForState, waitForText } from "./test-support";
|
|
13
13
|
|
|
14
14
|
const PLAN = `# Migration Plan
|
|
15
15
|
|
|
@@ -20,7 +20,7 @@ let home: string;
|
|
|
20
20
|
let vault: string;
|
|
21
21
|
let server: DaemonServer;
|
|
22
22
|
let session: ReviewSession;
|
|
23
|
-
let
|
|
23
|
+
let restoreUserConfig: () => void;
|
|
24
24
|
|
|
25
25
|
beforeEach(() => {
|
|
26
26
|
home = mkdtempSync(join(tmpdir(), "cueloop-app-export-"));
|
|
@@ -28,8 +28,7 @@ beforeEach(() => {
|
|
|
28
28
|
mkdirSync(vault);
|
|
29
29
|
const configPath = join(home, "config.toml");
|
|
30
30
|
writeFileSync(configPath, `[integrations.obsidian]\nvault = ${JSON.stringify(vault)}\nexportOn = "resolve"\n`);
|
|
31
|
-
|
|
32
|
-
process.env.CUELOOP_CONFIG = configPath;
|
|
31
|
+
restoreUserConfig = isolateUserConfig(home, "config.toml");
|
|
33
32
|
server = new DaemonServer({ home, idleExitMs: 0 });
|
|
34
33
|
server.start();
|
|
35
34
|
session = server.core.sessionCreate({
|
|
@@ -38,8 +37,7 @@ beforeEach(() => {
|
|
|
38
37
|
});
|
|
39
38
|
});
|
|
40
39
|
afterEach(() => {
|
|
41
|
-
|
|
42
|
-
else process.env.CUELOOP_CONFIG = savedConfigEnv;
|
|
40
|
+
restoreUserConfig();
|
|
43
41
|
server.stop();
|
|
44
42
|
rmSync(home, { recursive: true, force: true });
|
|
45
43
|
});
|
|
@@ -10,7 +10,7 @@ import { DaemonServer } from "@cueloop/daemon";
|
|
|
10
10
|
import type { ReviewSession } from "@cueloop/schema";
|
|
11
11
|
import { App } from "./App";
|
|
12
12
|
import { DARK as T } from "./theme";
|
|
13
|
-
import { press, settle, typeText as type, waitForState, waitForText, waitForTextGone } from "./test-support";
|
|
13
|
+
import { isolateUserConfig, press, settle, typeText as type, waitForState, waitForText, waitForTextGone } from "./test-support";
|
|
14
14
|
|
|
15
15
|
const PLAN = `# Migration Plan
|
|
16
16
|
|
|
@@ -27,14 +27,11 @@ The daemon persists sessions to disk atomically.
|
|
|
27
27
|
let home: string;
|
|
28
28
|
let server: DaemonServer;
|
|
29
29
|
let session: ReviewSession;
|
|
30
|
-
let
|
|
30
|
+
let restoreUserConfig: () => void;
|
|
31
31
|
|
|
32
32
|
beforeEach(() => {
|
|
33
33
|
home = mkdtempSync(join(tmpdir(), "cueloop-plan-review-"));
|
|
34
|
-
|
|
35
|
-
// not whatever review_state they last persisted (CI runs clean already).
|
|
36
|
-
priorConfig = process.env.CUELOOP_CONFIG;
|
|
37
|
-
process.env.CUELOOP_CONFIG = join(home, "no-config.toml");
|
|
34
|
+
restoreUserConfig = isolateUserConfig(home);
|
|
38
35
|
server = new DaemonServer({ home, idleExitMs: 0 });
|
|
39
36
|
server.start();
|
|
40
37
|
session = server.core.sessionCreate({
|
|
@@ -43,8 +40,7 @@ beforeEach(() => {
|
|
|
43
40
|
});
|
|
44
41
|
});
|
|
45
42
|
afterEach(() => {
|
|
46
|
-
|
|
47
|
-
else process.env.CUELOOP_CONFIG = priorConfig;
|
|
43
|
+
restoreUserConfig();
|
|
48
44
|
server.stop();
|
|
49
45
|
rmSync(home, { recursive: true, force: true });
|
|
50
46
|
});
|
|
@@ -250,7 +246,7 @@ describe("inline compose keeps the anchor painted", () => {
|
|
|
250
246
|
// the whole cursor block is the anchor here; check the highlight landed
|
|
251
247
|
const stored = server.core.sessionGet(session.id);
|
|
252
248
|
expect(stored.annotations.length).toBe(1);
|
|
253
|
-
expect(backgroundsOf(setup, stored.annotations[0]!.anchor.quote.slice(0, 20))).toContain(T.
|
|
249
|
+
expect(backgroundsOf(setup, stored.annotations[0]!.anchor.quote.slice(0, 20))).toContain(T.markCommentBackground);
|
|
254
250
|
// renderApp plus this many frame-waits grazes the 5s default on a loaded CI
|
|
255
251
|
// runner, and the whole-suite publish lane has timed even 15s out; give the
|
|
256
252
|
// heaviest frame-wait chain generous headroom so runner load cannot flake it.
|
|
@@ -396,7 +392,7 @@ describe("the document selects, the rail edits", () => {
|
|
|
396
392
|
await press(setup, "enter");
|
|
397
393
|
await waitForText(setup, "COMMENT · pending");
|
|
398
394
|
expect(server.core.sessionGet(session.id).annotations.length).toBe(1);
|
|
399
|
-
expect(backgroundsOf(setup, "persists sessions")).toContain(T.
|
|
395
|
+
expect(backgroundsOf(setup, "persists sessions")).toContain(T.markCommentBackground);
|
|
400
396
|
|
|
401
397
|
// Act
|
|
402
398
|
await press(setup, "x");
|
|
@@ -404,7 +400,7 @@ describe("the document selects, the rail edits", () => {
|
|
|
404
400
|
// Assert
|
|
405
401
|
await waitForTextGone(setup, "COMMENT · pending");
|
|
406
402
|
expect(server.core.sessionGet(session.id).annotations.length).toBe(0);
|
|
407
|
-
expect(backgroundsOf(setup, "persists sessions")).not.toContain(T.
|
|
403
|
+
expect(backgroundsOf(setup, "persists sessions")).not.toContain(T.markCommentBackground);
|
|
408
404
|
});
|
|
409
405
|
});
|
|
410
406
|
|
|
@@ -437,7 +433,7 @@ describe("addressed annotations leave the open list", () => {
|
|
|
437
433
|
expect(frame).toContain("✓ 1 addressed by revision");
|
|
438
434
|
expect(frame).not.toContain("settled note");
|
|
439
435
|
expect(frame).toContain("Review (1)"); // the addressed card no longer counts as pending
|
|
440
|
-
expect(backgroundsOf(setup, "The daemon")).not.toContain(T.
|
|
436
|
+
expect(backgroundsOf(setup, "The daemon")).not.toContain(T.markCommentBackground); // no highlight paint
|
|
441
437
|
});
|
|
442
438
|
});
|
|
443
439
|
|
|
@@ -9,7 +9,7 @@ import { testRender } from "@opentui/react/test-utils";
|
|
|
9
9
|
import { DaemonServer } from "@cueloop/daemon";
|
|
10
10
|
import { makeAnchor, parseBlocks, type ReviewSession } from "@cueloop/schema";
|
|
11
11
|
import { App } from "./App";
|
|
12
|
-
import { press, waitForText } from "./test-support";
|
|
12
|
+
import { isolateUserConfig, press, waitForText } from "./test-support";
|
|
13
13
|
|
|
14
14
|
const PLAN = `# Migration Plan
|
|
15
15
|
|
|
@@ -24,11 +24,13 @@ The daemon persists sessions to disk atomically.
|
|
|
24
24
|
`;
|
|
25
25
|
|
|
26
26
|
let home: string;
|
|
27
|
+
let restoreUserConfig: () => void;
|
|
27
28
|
let server: DaemonServer;
|
|
28
29
|
let session: ReviewSession;
|
|
29
30
|
|
|
30
31
|
beforeEach(() => {
|
|
31
32
|
home = mkdtempSync(join(tmpdir(), "cueloop-observer-"));
|
|
33
|
+
restoreUserConfig = isolateUserConfig(home);
|
|
32
34
|
server = new DaemonServer({ home, idleExitMs: 0 });
|
|
33
35
|
server.start();
|
|
34
36
|
session = server.core.sessionCreate({
|
|
@@ -37,6 +39,7 @@ beforeEach(() => {
|
|
|
37
39
|
});
|
|
38
40
|
});
|
|
39
41
|
afterEach(() => {
|
|
42
|
+
restoreUserConfig();
|
|
40
43
|
server.stop();
|
|
41
44
|
rmSync(home, { recursive: true, force: true });
|
|
42
45
|
});
|
|
@@ -9,7 +9,7 @@ import { testRender } from "@opentui/react/test-utils";
|
|
|
9
9
|
import { DaemonServer } from "@cueloop/daemon";
|
|
10
10
|
import type { ReviewSession } from "@cueloop/schema";
|
|
11
11
|
import { App } from "./App";
|
|
12
|
-
import { waitForText } from "./test-support";
|
|
12
|
+
import { isolateUserConfig, waitForText } from "./test-support";
|
|
13
13
|
|
|
14
14
|
const PLAN = `# Render Plan
|
|
15
15
|
|
|
@@ -30,11 +30,13 @@ Deploys block below the threshold.
|
|
|
30
30
|
`;
|
|
31
31
|
|
|
32
32
|
let home: string;
|
|
33
|
+
let restoreUserConfig: () => void;
|
|
33
34
|
let server: DaemonServer;
|
|
34
35
|
let session: ReviewSession;
|
|
35
36
|
|
|
36
37
|
beforeEach(() => {
|
|
37
38
|
home = mkdtempSync(join(tmpdir(), "cueloop-render-"));
|
|
39
|
+
restoreUserConfig = isolateUserConfig(home);
|
|
38
40
|
server = new DaemonServer({ home, idleExitMs: 0 });
|
|
39
41
|
server.start();
|
|
40
42
|
session = server.core.sessionCreate({
|
|
@@ -43,6 +45,7 @@ beforeEach(() => {
|
|
|
43
45
|
});
|
|
44
46
|
});
|
|
45
47
|
afterEach(() => {
|
|
48
|
+
restoreUserConfig();
|
|
46
49
|
server.stop();
|
|
47
50
|
rmSync(home, { recursive: true, force: true });
|
|
48
51
|
});
|
|
@@ -10,7 +10,7 @@ import { DaemonServer } from "@cueloop/daemon";
|
|
|
10
10
|
import type { ReviewSession } from "@cueloop/schema";
|
|
11
11
|
import { App } from "./App";
|
|
12
12
|
import { loadConfig } from "./config";
|
|
13
|
-
import { press, waitForText, waitForTextGone } from "./test-support";
|
|
13
|
+
import { isolateUserConfig, press, waitForText, waitForTextGone } from "./test-support";
|
|
14
14
|
|
|
15
15
|
const PLAN = "# Plan\n\nShip the thing.\n";
|
|
16
16
|
|
|
@@ -18,11 +18,12 @@ let home: string;
|
|
|
18
18
|
let server: DaemonServer;
|
|
19
19
|
let session: ReviewSession;
|
|
20
20
|
let configPath: string;
|
|
21
|
+
let restoreUserConfig: () => void;
|
|
21
22
|
|
|
22
23
|
beforeEach(() => {
|
|
23
24
|
home = mkdtempSync(join(tmpdir(), "cueloop-panel-"));
|
|
24
25
|
configPath = join(home, "config.toml");
|
|
25
|
-
|
|
26
|
+
restoreUserConfig = isolateUserConfig(home, "config.toml");
|
|
26
27
|
server = new DaemonServer({ home, idleExitMs: 0 });
|
|
27
28
|
server.start();
|
|
28
29
|
session = server.core.sessionCreate({
|
|
@@ -31,7 +32,7 @@ beforeEach(() => {
|
|
|
31
32
|
});
|
|
32
33
|
});
|
|
33
34
|
afterEach(() => {
|
|
34
|
-
|
|
35
|
+
restoreUserConfig();
|
|
35
36
|
server.stop();
|
|
36
37
|
rmSync(home, { recursive: true, force: true });
|
|
37
38
|
});
|
|
@@ -9,7 +9,7 @@ import { testRender } from "@opentui/react/test-utils";
|
|
|
9
9
|
import { DaemonServer } from "@cueloop/daemon";
|
|
10
10
|
import { makeAnchor, parseBlocks, type ReviewSession } from "@cueloop/schema";
|
|
11
11
|
import { App } from "./App";
|
|
12
|
-
import { press, settle, waitForText, waitForTextGone } from "./test-support";
|
|
12
|
+
import { isolateUserConfig, press, settle, waitForText, waitForTextGone } from "./test-support";
|
|
13
13
|
|
|
14
14
|
const PLAN = `# Migration Plan
|
|
15
15
|
|
|
@@ -24,11 +24,13 @@ The daemon persists sessions to disk atomically.
|
|
|
24
24
|
`;
|
|
25
25
|
|
|
26
26
|
let home: string;
|
|
27
|
+
let restoreUserConfig: () => void;
|
|
27
28
|
let server: DaemonServer;
|
|
28
29
|
let session: ReviewSession;
|
|
29
30
|
|
|
30
31
|
beforeEach(() => {
|
|
31
32
|
home = mkdtempSync(join(tmpdir(), "cueloop-confirm-"));
|
|
33
|
+
restoreUserConfig = isolateUserConfig(home);
|
|
32
34
|
server = new DaemonServer({ home, idleExitMs: 0 });
|
|
33
35
|
server.start();
|
|
34
36
|
session = server.core.sessionCreate({
|
|
@@ -37,6 +39,7 @@ beforeEach(() => {
|
|
|
37
39
|
});
|
|
38
40
|
});
|
|
39
41
|
afterEach(() => {
|
|
42
|
+
restoreUserConfig();
|
|
40
43
|
server.stop();
|
|
41
44
|
rmSync(home, { recursive: true, force: true });
|
|
42
45
|
});
|
package/src/App.test.tsx
CHANGED
|
@@ -9,7 +9,7 @@ import { testRender } from "@opentui/react/test-utils";
|
|
|
9
9
|
import { DaemonServer } from "@cueloop/daemon";
|
|
10
10
|
import type { ReviewSession } from "@cueloop/schema";
|
|
11
11
|
import { App } from "./App";
|
|
12
|
-
import { press, typeText as type, waitForState, waitForText } from "./test-support";
|
|
12
|
+
import { isolateUserConfig, press, typeText as type, waitForState, waitForText } from "./test-support";
|
|
13
13
|
|
|
14
14
|
const PLAN = `# Migration Plan
|
|
15
15
|
|
|
@@ -24,11 +24,13 @@ The daemon persists sessions to disk atomically.
|
|
|
24
24
|
`;
|
|
25
25
|
|
|
26
26
|
let home: string;
|
|
27
|
+
let restoreUserConfig: () => void;
|
|
27
28
|
let server: DaemonServer;
|
|
28
29
|
let session: ReviewSession;
|
|
29
30
|
|
|
30
31
|
beforeEach(() => {
|
|
31
32
|
home = mkdtempSync(join(tmpdir(), "cueloop-app-"));
|
|
33
|
+
restoreUserConfig = isolateUserConfig(home);
|
|
32
34
|
server = new DaemonServer({ home, idleExitMs: 0 });
|
|
33
35
|
server.start();
|
|
34
36
|
session = server.core.sessionCreate({
|
|
@@ -37,6 +39,7 @@ beforeEach(() => {
|
|
|
37
39
|
});
|
|
38
40
|
});
|
|
39
41
|
afterEach(() => {
|
|
42
|
+
restoreUserConfig();
|
|
40
43
|
server.stop();
|
|
41
44
|
rmSync(home, { recursive: true, force: true });
|
|
42
45
|
});
|
package/src/App.tsx
CHANGED
|
@@ -513,7 +513,7 @@ export function App({ home, sessionId, readOnly = false, onExit, clock, openClie
|
|
|
513
513
|
return (
|
|
514
514
|
<ThemeProvider theme={theme}>
|
|
515
515
|
<box
|
|
516
|
-
style={{ flexDirection: "column", width: "100%", height: "100%", backgroundColor: theme.
|
|
516
|
+
style={{ flexDirection: "column", width: "100%", height: "100%", backgroundColor: theme.background }}
|
|
517
517
|
onMouseDrag={(event: MouseEvent) => {
|
|
518
518
|
if (!dividerDragging || reviewMode !== "expanded") return;
|
|
519
519
|
const next = widthFromMouseColumn(event.x, terminalWidth);
|
package/src/App.walk.test.tsx
CHANGED
|
@@ -10,7 +10,7 @@ import { DaemonServer } from "@cueloop/daemon";
|
|
|
10
10
|
import type { ReviewSession } from "@cueloop/schema";
|
|
11
11
|
import { App } from "./App";
|
|
12
12
|
import { DARK } from "./theme";
|
|
13
|
-
import { press, waitForState, waitForText, waitForTextGone } from "./test-support";
|
|
13
|
+
import { isolateUserConfig, press, waitForState, waitForText, waitForTextGone } from "./test-support";
|
|
14
14
|
|
|
15
15
|
const PATCH = `diff --git a/src/a.ts b/src/a.ts
|
|
16
16
|
index 111..222 100644
|
|
@@ -38,11 +38,13 @@ index 555..666 100644
|
|
|
38
38
|
`;
|
|
39
39
|
|
|
40
40
|
let home: string;
|
|
41
|
+
let restoreUserConfig: () => void;
|
|
41
42
|
let server: DaemonServer;
|
|
42
43
|
let session: ReviewSession;
|
|
43
44
|
|
|
44
45
|
beforeEach(() => {
|
|
45
46
|
home = mkdtempSync(join(tmpdir(), "cueloop-walk-"));
|
|
47
|
+
restoreUserConfig = isolateUserConfig(home);
|
|
46
48
|
server = new DaemonServer({ home, idleExitMs: 0 });
|
|
47
49
|
server.start();
|
|
48
50
|
session = server.core.sessionCreate({
|
|
@@ -51,6 +53,7 @@ beforeEach(() => {
|
|
|
51
53
|
});
|
|
52
54
|
});
|
|
53
55
|
afterEach(() => {
|
|
56
|
+
restoreUserConfig();
|
|
54
57
|
server.stop();
|
|
55
58
|
rmSync(home, { recursive: true, force: true });
|
|
56
59
|
});
|
|
@@ -236,7 +239,7 @@ describe("the guided walk", () => {
|
|
|
236
239
|
// Assert
|
|
237
240
|
// before the walk the sheet's added line wears the insertion color
|
|
238
241
|
await waitForText(setup, "+new line");
|
|
239
|
-
expect(foregroundsOf(setup, "+new line")).toContain(DARK.
|
|
242
|
+
expect(foregroundsOf(setup, "+new line")).toContain(DARK.insertedForeground);
|
|
240
243
|
|
|
241
244
|
// Act
|
|
242
245
|
await press(setup, "w");
|
|
@@ -246,7 +249,7 @@ describe("the guided walk", () => {
|
|
|
246
249
|
// dimmed: the sheet line drops to the dim token...
|
|
247
250
|
expect(foregroundsOf(setup, "+new line")).toEqual([DARK.textDim]);
|
|
248
251
|
// ...while the wizard preview keeps the insertion color
|
|
249
|
-
expect(foregroundsOf(setup, "+export const b = 3;")).toContain(DARK.
|
|
252
|
+
expect(foregroundsOf(setup, "+export const b = 3;")).toContain(DARK.insertedForeground);
|
|
250
253
|
});
|
|
251
254
|
|
|
252
255
|
test("walking a resolved review answers read-only", async () => {
|
|
@@ -27,7 +27,7 @@ export function CompletionOverlay({ verdict, completion, status, returnsTo, them
|
|
|
27
27
|
style={{
|
|
28
28
|
width: "100%",
|
|
29
29
|
height: "100%",
|
|
30
|
-
backgroundColor: tokens.
|
|
30
|
+
backgroundColor: tokens.background,
|
|
31
31
|
flexDirection: "column",
|
|
32
32
|
justifyContent: "center",
|
|
33
33
|
alignItems: "center",
|
|
@@ -8,13 +8,13 @@ export const meta: StoryMeta = { title: "DiffSheet" };
|
|
|
8
8
|
|
|
9
9
|
export const SignedRows: Story = {
|
|
10
10
|
render: () => <DiffSheet rows={fixtureDiffRows()} cursor={0} annotations={[]} />,
|
|
11
|
-
expectedColors: [DARK.
|
|
11
|
+
expectedColors: [DARK.insertedForeground, DARK.deletedForeground],
|
|
12
12
|
size: { width: 90, height: 16 },
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export const CursorOnAddedLine: Story = {
|
|
16
16
|
render: () => <DiffSheet rows={fixtureDiffRows()} cursor={4} annotations={[]} />,
|
|
17
|
-
expectedColors: [DARK.
|
|
17
|
+
expectedColors: [DARK.cursorBackground],
|
|
18
18
|
size: { width: 90, height: 16 },
|
|
19
19
|
};
|
|
20
20
|
|
|
@@ -39,6 +39,6 @@ export const AnnotatedLine: Story = {
|
|
|
39
39
|
]}
|
|
40
40
|
/>
|
|
41
41
|
),
|
|
42
|
-
expectedColors: [DARK.
|
|
42
|
+
expectedColors: [DARK.markCommentBackground],
|
|
43
43
|
size: { width: 90, height: 16 },
|
|
44
44
|
};
|
|
@@ -100,10 +100,10 @@ function DiffChunk({
|
|
|
100
100
|
// the renderable's config parser requires the gutter key to be present
|
|
101
101
|
const colors = new Map<number, { gutter: string | undefined; content: string }>();
|
|
102
102
|
if (segment.annotation) {
|
|
103
|
-
colors.set(segment.rows.length - 1, { gutter: undefined, content: tokens.
|
|
103
|
+
colors.set(segment.rows.length - 1, { gutter: undefined, content: tokens.markCommentBackground });
|
|
104
104
|
}
|
|
105
105
|
if (cursorInChunk >= 0 && cursorInChunk < segment.rows.length) {
|
|
106
|
-
colors.set(cursorInChunk, { gutter: tokens.
|
|
106
|
+
colors.set(cursorInChunk, { gutter: tokens.cursorBackground, content: tokens.cursorBackground });
|
|
107
107
|
}
|
|
108
108
|
return colors;
|
|
109
109
|
}, [segment, cursorInChunk, tokens]);
|
|
@@ -124,7 +124,7 @@ function DiffChunk({
|
|
|
124
124
|
<line-number
|
|
125
125
|
ref={gutterRef}
|
|
126
126
|
fg={tokens.textDim}
|
|
127
|
-
bg={tokens.
|
|
127
|
+
bg={tokens.background}
|
|
128
128
|
minWidth={4}
|
|
129
129
|
paddingRight={1}
|
|
130
130
|
lineNumbers={lineNumbers}
|
|
@@ -133,14 +133,14 @@ function DiffChunk({
|
|
|
133
133
|
<text style={{ wrapMode: "none" }} selectable={false}>
|
|
134
134
|
{segment.rows.map((row, lineIndex) => {
|
|
135
135
|
const sign = row.kind === "add" ? "+" : row.kind === "del" ? "-" : " ";
|
|
136
|
-
const foreground = row.kind === "add" ? tokens.
|
|
136
|
+
const foreground = row.kind === "add" ? tokens.insertedForeground : row.kind === "del" ? tokens.deletedForeground : tokens.textMuted;
|
|
137
137
|
const isCursorRow = lineIndex === cursorInChunk;
|
|
138
138
|
const isAnnotatedRow = segment.annotation !== null && lineIndex === segment.rows.length - 1;
|
|
139
139
|
return (
|
|
140
140
|
<span
|
|
141
141
|
key={lineIndex}
|
|
142
142
|
fg={foreground}
|
|
143
|
-
bg={isCursorRow ? tokens.
|
|
143
|
+
bg={isCursorRow ? tokens.cursorBackground : isAnnotatedRow ? tokens.markCommentBackground : undefined}
|
|
144
144
|
>
|
|
145
145
|
{(lineIndex > 0 ? "\n" : "") + sign + rowLine(row)}
|
|
146
146
|
</span>
|
|
@@ -215,13 +215,13 @@ export function DiffSheet({ rows, cursor, annotations, focusedAnnotationId, them
|
|
|
215
215
|
const isCursor = segment.rowIndex === cursor;
|
|
216
216
|
if (segment.row.kind === "file") {
|
|
217
217
|
return (
|
|
218
|
-
<text key={segmentIndex} fg={tokens.text} bg={isCursor ? tokens.
|
|
218
|
+
<text key={segmentIndex} fg={tokens.text} bg={isCursor ? tokens.cursorBackground : tokens.panel}>
|
|
219
219
|
{isCursor ? "▎" : " "}■ {rowLine(segment.row)}
|
|
220
220
|
</text>
|
|
221
221
|
);
|
|
222
222
|
}
|
|
223
223
|
return (
|
|
224
|
-
<text key={segmentIndex} fg={tokens.blue} bg={isCursor ? tokens.
|
|
224
|
+
<text key={segmentIndex} fg={tokens.blue} bg={isCursor ? tokens.cursorBackground : undefined}>
|
|
225
225
|
{isCursor ? "▎" : " "}
|
|
226
226
|
{rowLine(segment.row)}
|
|
227
227
|
</text>
|
|
@@ -8,7 +8,7 @@ export const meta: StoryMeta = { title: "InboxList" };
|
|
|
8
8
|
|
|
9
9
|
export const TwoPending: Story = {
|
|
10
10
|
render: () => <InboxList inbox={[fixturePlanSession(), fixtureDiffSession()]} cursor={0} />,
|
|
11
|
-
expectedColors: [DARK.accent, DARK.
|
|
11
|
+
expectedColors: [DARK.accent, DARK.cursorBackground],
|
|
12
12
|
size: { width: 80, height: 12 },
|
|
13
13
|
};
|
|
14
14
|
|
|
@@ -23,7 +23,7 @@ export interface InboxListProps {
|
|
|
23
23
|
export function InboxList({ inbox, cursor, onRequestDelete, theme }: InboxListProps): React.ReactNode {
|
|
24
24
|
const tokens = useComponentTheme(theme);
|
|
25
25
|
return (
|
|
26
|
-
<box style={{ flexDirection: "column", width: "100%", height: "100%", backgroundColor: tokens.
|
|
26
|
+
<box style={{ flexDirection: "column", width: "100%", height: "100%", backgroundColor: tokens.background, padding: 1 }}>
|
|
27
27
|
<text fg={tokens.accent}>cueloop · inbox ({inbox.length} pending)</text>
|
|
28
28
|
<text> </text>
|
|
29
29
|
{inbox.length === 0 ? (
|
|
@@ -33,7 +33,7 @@ export function InboxList({ inbox, cursor, onRequestDelete, theme }: InboxListPr
|
|
|
33
33
|
const selected = index === cursor;
|
|
34
34
|
const title = session.artifact.meta.title ?? session.id;
|
|
35
35
|
return (
|
|
36
|
-
<box key={session.id} style={{ flexDirection: "row", backgroundColor: selected ? tokens.
|
|
36
|
+
<box key={session.id} style={{ flexDirection: "row", backgroundColor: selected ? tokens.cursorBackground : undefined }}>
|
|
37
37
|
<text fg={selected ? tokens.text : tokens.textMuted}>
|
|
38
38
|
{selected ? "▸ " : " "}
|
|
39
39
|
{title} · {session.workspace.branch} · {session.artifact.type}
|
|
@@ -21,7 +21,7 @@ export const AnnotatedPlan: Story = {
|
|
|
21
21
|
{...callbacks}
|
|
22
22
|
/>
|
|
23
23
|
),
|
|
24
|
-
expectedColors: [DARK.
|
|
24
|
+
expectedColors: [DARK.markCommentBackground, DARK.markSuggestionBackground, DARK.cursorBackground],
|
|
25
25
|
size: { width: 90, height: 28 },
|
|
26
26
|
};
|
|
27
27
|
|
|
@@ -193,7 +193,7 @@ export const PlanSheet = forwardRef<PlanSheetHandle, PlanSheetProps>(function Pl
|
|
|
193
193
|
<span fg={tokens.textDim}>{marker(block)}</span>
|
|
194
194
|
</text>
|
|
195
195
|
<text
|
|
196
|
-
bg={isCursor ? tokens.
|
|
196
|
+
bg={isCursor ? tokens.cursorBackground : undefined}
|
|
197
197
|
selectable
|
|
198
198
|
selectionBg={tokens.accent}
|
|
199
199
|
selectionFg={tokens.accentInk}
|
|
@@ -228,7 +228,7 @@ export const PlanSheet = forwardRef<PlanSheetHandle, PlanSheetProps>(function Pl
|
|
|
228
228
|
theme={theme}
|
|
229
229
|
/>
|
|
230
230
|
{editOrphanCount > 0 ? (
|
|
231
|
-
<box style={{ height: 1, backgroundColor: tokens.
|
|
231
|
+
<box style={{ height: 1, backgroundColor: tokens.markCommentBackground, paddingLeft: 2 }}>
|
|
232
232
|
<text fg={tokens.red}>
|
|
233
233
|
{editOrphanCount} annotation{editOrphanCount === 1 ? "" : "s"} no longer match - the passage was removed.
|
|
234
234
|
</text>
|
|
@@ -312,13 +312,13 @@ function runStyle(run: StyleRun, block: DisplayBlock, tokens: Theme): { fg?: str
|
|
|
312
312
|
const struck = block.type === "del";
|
|
313
313
|
switch (run.role) {
|
|
314
314
|
case "ins":
|
|
315
|
-
return { fg: tokens.
|
|
315
|
+
return { fg: tokens.insertedForeground };
|
|
316
316
|
case "del":
|
|
317
|
-
return { fg: tokens.
|
|
317
|
+
return { fg: tokens.deletedForeground };
|
|
318
318
|
case "mark-comment":
|
|
319
|
-
return { fg: tokens.text, bg: tokens.
|
|
319
|
+
return { fg: tokens.text, bg: tokens.markCommentBackground };
|
|
320
320
|
case "mark-suggestion":
|
|
321
|
-
return { fg: tokens.text, bg: tokens.
|
|
321
|
+
return { fg: tokens.text, bg: tokens.markSuggestionBackground };
|
|
322
322
|
case "mark-focus":
|
|
323
323
|
return { fg: tokens.accentInk, bg: tokens.accent };
|
|
324
324
|
case "kspan":
|
|
@@ -16,7 +16,7 @@ export const RenameAuthor: Story = {
|
|
|
16
16
|
onInput={() => {}}
|
|
17
17
|
/>
|
|
18
18
|
),
|
|
19
|
-
expectedColors: [DARK.
|
|
19
|
+
expectedColors: [DARK.elevated, DARK.accent],
|
|
20
20
|
size: { width: 64, height: 16 },
|
|
21
21
|
};
|
|
22
22
|
|
|
@@ -31,6 +31,6 @@ export const NameYourself: Story = {
|
|
|
31
31
|
onInput={() => {}}
|
|
32
32
|
/>
|
|
33
33
|
),
|
|
34
|
-
expectedColors: [DARK.
|
|
34
|
+
expectedColors: [DARK.elevated],
|
|
35
35
|
size: { width: 64, height: 16 },
|
|
36
36
|
};
|
|
@@ -27,7 +27,7 @@ const panelCallbacks = { onDividerGrab: () => {}, onToggle: () => {} };
|
|
|
27
27
|
/** The plan area to the left of the divider, so the layout reads in context. */
|
|
28
28
|
function PanelFrame({ children }: { children: React.ReactNode }): React.ReactNode {
|
|
29
29
|
return (
|
|
30
|
-
<box style={{ flexDirection: "row", width: "100%", height: "100%", backgroundColor: DARK.
|
|
30
|
+
<box style={{ flexDirection: "row", width: "100%", height: "100%", backgroundColor: DARK.background }}>
|
|
31
31
|
<box style={{ flexGrow: 1, paddingLeft: 1 }}>
|
|
32
32
|
<text fg={DARK.textMuted}>The plan takes the width the panel hands back.</text>
|
|
33
33
|
</box>
|
|
@@ -40,7 +40,7 @@ export const FirstFile: Story = {
|
|
|
40
40
|
/>
|
|
41
41
|
),
|
|
42
42
|
// the main card wears the accent border; stats carry the +/- colors
|
|
43
|
-
expectedColors: [DARK.accent, DARK.green, DARK.red, DARK.
|
|
43
|
+
expectedColors: [DARK.accent, DARK.green, DARK.red, DARK.insertedForeground, DARK.deletedForeground],
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
export const WithAgentNote: Story = {
|
|
@@ -118,7 +118,7 @@ export function WalkWizard({
|
|
|
118
118
|
<text
|
|
119
119
|
key={previewIndex}
|
|
120
120
|
style={{ wrapMode: "none" }}
|
|
121
|
-
fg={previewLine.sign === "+" ? tokens.
|
|
121
|
+
fg={previewLine.sign === "+" ? tokens.insertedForeground : tokens.deletedForeground}
|
|
122
122
|
>
|
|
123
123
|
{previewLine.sign + previewLine.text}
|
|
124
124
|
</text>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Centered modal
|
|
3
|
-
*
|
|
2
|
+
* Centered modal: a full-screen layer (backdrop token, transparent by
|
|
3
|
+
* default) centers a bordered, unpainted panel over the session - the frame
|
|
4
|
+
* delineates the dialog, the content behind stays visible.
|
|
4
5
|
* `isOpen` keeps the mount decision with the caller-visible prop vocabulary.
|
|
5
6
|
*/
|
|
6
7
|
|
|
@@ -41,8 +42,6 @@ export function Dialog({ isOpen, title, width, height, theme, children }: Dialog
|
|
|
41
42
|
border: true,
|
|
42
43
|
borderStyle: FRAME_BORDER_STYLE,
|
|
43
44
|
borderColor: tokens.accent,
|
|
44
|
-
/* Floating surface: painted so dialog text never mixes with the page behind. */
|
|
45
|
-
backgroundColor: tokens.elevated,
|
|
46
45
|
flexDirection: "column",
|
|
47
46
|
}}
|
|
48
47
|
title={title}
|
|
@@ -38,7 +38,7 @@ function StoriesApp({ stories, onExit }: { stories: LoadedStory[]; onExit: () =>
|
|
|
38
38
|
|
|
39
39
|
return (
|
|
40
40
|
<ThemeProvider theme={DARK}>
|
|
41
|
-
<box style={{ flexDirection: "column", width: "100%", height: "100%", backgroundColor: DARK.
|
|
41
|
+
<box style={{ flexDirection: "column", width: "100%", height: "100%", backgroundColor: DARK.background }}>
|
|
42
42
|
<box style={{ height: 1, backgroundColor: DARK.panel, paddingLeft: 1 }}>
|
|
43
43
|
<text fg={DARK.text}>
|
|
44
44
|
<span fg={DARK.accent}>cueloop stories</span>
|
|
@@ -9,6 +9,7 @@ import { describe, expect, test } from "bun:test";
|
|
|
9
9
|
import { testRender } from "@opentui/react/test-utils";
|
|
10
10
|
import { RGBA } from "@opentui/core";
|
|
11
11
|
import { componentFilesMissingStories, loadStories } from "./story";
|
|
12
|
+
import { allowEventLoopUpdates } from "../test-support";
|
|
12
13
|
|
|
13
14
|
const stories = await loadStories();
|
|
14
15
|
|
|
@@ -35,6 +36,7 @@ describe("stories catalog", () => {
|
|
|
35
36
|
|
|
36
37
|
// Act
|
|
37
38
|
const setup = await testRender(story.render(), size);
|
|
39
|
+
allowEventLoopUpdates();
|
|
38
40
|
await setup.waitForVisualIdle();
|
|
39
41
|
const frame = setup.captureCharFrame();
|
|
40
42
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { loadConfig } from "./config";
|
|
6
|
+
import { isolateUserConfig } from "./test-support";
|
|
7
|
+
|
|
8
|
+
describe("isolateUserConfig", () => {
|
|
9
|
+
test("loadConfig sees defaults while isolated and the env is restored after", () => {
|
|
10
|
+
// Arrange
|
|
11
|
+
const home = mkdtempSync(join(tmpdir(), "cueloop-isolate-"));
|
|
12
|
+
const userConfigPath = join(home, "config.toml");
|
|
13
|
+
writeFileSync(userConfigPath, '[ui]\nreview_state = "compact"\n');
|
|
14
|
+
const priorEnv = process.env.CUELOOP_CONFIG;
|
|
15
|
+
process.env.CUELOOP_CONFIG = userConfigPath;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
// Act
|
|
19
|
+
const restoreUserConfig = isolateUserConfig(home);
|
|
20
|
+
const isolated = loadConfig({ repoRoot: home });
|
|
21
|
+
restoreUserConfig();
|
|
22
|
+
const restored = loadConfig({ repoRoot: home });
|
|
23
|
+
|
|
24
|
+
// Assert
|
|
25
|
+
expect(isolated.ui.reviewState).toBe("expanded");
|
|
26
|
+
expect(process.env.CUELOOP_CONFIG).toBe(userConfigPath);
|
|
27
|
+
expect(restored.ui.reviewState).toBe("compact");
|
|
28
|
+
} finally {
|
|
29
|
+
if (priorEnv === undefined) delete process.env.CUELOOP_CONFIG;
|
|
30
|
+
else process.env.CUELOOP_CONFIG = priorEnv;
|
|
31
|
+
rmSync(home, { recursive: true, force: true });
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("restore removes the variable when nothing was set before", () => {
|
|
36
|
+
// Arrange
|
|
37
|
+
const home = mkdtempSync(join(tmpdir(), "cueloop-isolate-"));
|
|
38
|
+
const priorEnv = process.env.CUELOOP_CONFIG;
|
|
39
|
+
delete process.env.CUELOOP_CONFIG;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
// Act
|
|
43
|
+
const restoreUserConfig = isolateUserConfig(home);
|
|
44
|
+
const pointedAt = process.env.CUELOOP_CONFIG ?? "";
|
|
45
|
+
restoreUserConfig();
|
|
46
|
+
|
|
47
|
+
// Assert
|
|
48
|
+
expect(pointedAt).toBe(join(home, "no-config.toml"));
|
|
49
|
+
expect(process.env.CUELOOP_CONFIG).toBeUndefined();
|
|
50
|
+
} finally {
|
|
51
|
+
if (priorEnv !== undefined) process.env.CUELOOP_CONFIG = priorEnv;
|
|
52
|
+
rmSync(home, { recursive: true, force: true });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
package/src/test-support.ts
CHANGED
|
@@ -6,16 +6,40 @@
|
|
|
6
6
|
* generous pass budgets.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { join } from "node:path";
|
|
9
10
|
import type { TestRendererSetup } from "@opentui/core/testing";
|
|
10
11
|
|
|
11
12
|
/** Pass budget for waits that include daemon or subprocess round-trips. */
|
|
12
13
|
export const WAIT_PASSES = { maxPasses: 400 };
|
|
13
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Isolate the user config: point CUELOOP_CONFIG into the test home so
|
|
17
|
+
* loadConfig never reads the developer's real ~/.config/cueloop (a persisted
|
|
18
|
+
* review_state would change what char frames render locally while CI stays
|
|
19
|
+
* clean). The default file name does not exist, so loadConfig returns
|
|
20
|
+
* defaults; pass a file name for suites that write and assert a config of
|
|
21
|
+
* their own. Call in beforeEach; invoke the returned restore in afterEach.
|
|
22
|
+
*/
|
|
23
|
+
export function isolateUserConfig(home: string, fileName = "no-config.toml"): () => void {
|
|
24
|
+
const priorUserConfig = process.env.CUELOOP_CONFIG;
|
|
25
|
+
process.env.CUELOOP_CONFIG = join(home, fileName);
|
|
26
|
+
return () => {
|
|
27
|
+
if (priorUserConfig === undefined) delete process.env.CUELOOP_CONFIG;
|
|
28
|
+
else process.env.CUELOOP_CONFIG = priorUserConfig;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The harness drives the real event loop by design; keep React's act warning off. */
|
|
33
|
+
export function allowEventLoopUpdates(): void {
|
|
34
|
+
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = false;
|
|
35
|
+
}
|
|
36
|
+
|
|
14
37
|
/**
|
|
15
38
|
* One macrotask yield lets the input parser and the React scheduler run,
|
|
16
39
|
* then a render pass commits the result before the visual-idle wait.
|
|
17
40
|
*/
|
|
18
41
|
export async function settle(setup: TestRendererSetup): Promise<void> {
|
|
42
|
+
allowEventLoopUpdates();
|
|
19
43
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
20
44
|
await setup.renderOnce();
|
|
21
45
|
await setup.waitForVisualIdle();
|
|
@@ -50,6 +74,7 @@ async function waitForFramePredicate(
|
|
|
50
74
|
setup: TestRendererSetup,
|
|
51
75
|
predicate: (frame: string) => boolean,
|
|
52
76
|
): Promise<string> {
|
|
77
|
+
allowEventLoopUpdates();
|
|
53
78
|
const deadline = Date.now() + WAIT_DEADLINE_MS;
|
|
54
79
|
for (;;) {
|
|
55
80
|
try {
|
|
@@ -64,6 +89,7 @@ async function waitForFramePredicate(
|
|
|
64
89
|
|
|
65
90
|
/** Wait until a state predicate holds (daemon round-trips included). */
|
|
66
91
|
export async function waitForState(setup: TestRendererSetup, predicate: () => boolean): Promise<void> {
|
|
92
|
+
allowEventLoopUpdates();
|
|
67
93
|
const deadline = Date.now() + WAIT_DEADLINE_MS;
|
|
68
94
|
for (;;) {
|
|
69
95
|
try {
|
package/src/theme.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
export interface Theme {
|
|
9
|
-
|
|
9
|
+
background: string;
|
|
10
10
|
panel: string;
|
|
11
11
|
elevated: string;
|
|
12
12
|
border: string;
|
|
@@ -18,12 +18,12 @@ export interface Theme {
|
|
|
18
18
|
green: string;
|
|
19
19
|
red: string;
|
|
20
20
|
blue: string;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
/**
|
|
21
|
+
cursorBackground: string;
|
|
22
|
+
markCommentBackground: string;
|
|
23
|
+
markSuggestionBackground: string;
|
|
24
|
+
insertedForeground: string;
|
|
25
|
+
deletedForeground: string;
|
|
26
|
+
/** Layer behind centered dialogs; transparent keeps the session visible. */
|
|
27
27
|
backdrop: string;
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -41,14 +41,14 @@ export function dimmedTheme(theme: Theme): Theme {
|
|
|
41
41
|
green: theme.textDim,
|
|
42
42
|
red: theme.textDim,
|
|
43
43
|
blue: theme.textDim,
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
insertedForeground: theme.textDim,
|
|
45
|
+
deletedForeground: theme.textDim,
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export const DARK: Theme = {
|
|
50
50
|
/* Unpainted so the terminal background shows through; override in [theme]. */
|
|
51
|
-
|
|
51
|
+
background: "transparent",
|
|
52
52
|
panel: "transparent",
|
|
53
53
|
elevated: "#23232f",
|
|
54
54
|
border: "#3a3a4a",
|
|
@@ -60,10 +60,10 @@ export const DARK: Theme = {
|
|
|
60
60
|
green: "#62d96b",
|
|
61
61
|
red: "#ff6b6b",
|
|
62
62
|
blue: "#84a6e8",
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
backdrop: "
|
|
63
|
+
cursorBackground: "#33262a",
|
|
64
|
+
markCommentBackground: "#3d2a2e",
|
|
65
|
+
markSuggestionBackground: "#20351f",
|
|
66
|
+
insertedForeground: "#62d96b",
|
|
67
|
+
deletedForeground: "#ff6b6b",
|
|
68
|
+
backdrop: "transparent",
|
|
69
69
|
}
|