@meistrari/remy-cli 1.25.0 → 1.26.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/remy.js +322 -124
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,6 +40,8 @@ On first use, Remy opens browser-based device authorization, validates your Codi
|
|
|
40
40
|
|
|
41
41
|
Run `remy update` to install a newer CLI release using the package manager that owns your installation (mise, Bun, pnpm, or npm). No sign-in is required. Use `remy --version` to print the installed version.
|
|
42
42
|
|
|
43
|
+
For mise installations, updates change the existing owning TOML or `.tool-versions` config to the exact new version and keep the previous installation available for running processes. Shared-cache npm installs are supported.
|
|
44
|
+
|
|
43
45
|
Stable versions check npm’s `latest` channel. Release candidates check both `rc` and `latest` and choose the newer version; Remy never downgrades. To switch from stable to RC, use the explicit `@rc` install command above.
|
|
44
46
|
|
|
45
47
|
Each interactive `remy` or `remy dashboard` startup checks for an update before sign-in or the splash. When one is available, type `y` or `yes`, then Enter, to upgrade. Press Enter or type `n` to continue with the current version. Lookup failures or a two-second timeout let startup continue. After a successful upgrade, Remy exits and asks you to run it again. Installation failures exit with an error so you can resolve the package-manager problem and retry.
|
|
@@ -68,7 +70,7 @@ The new-session request and conversation composers share path completion and att
|
|
|
68
70
|
|
|
69
71
|
Terminal follow-ups default to **Steer**, targeting the work currently in progress. Press `Ctrl+Enter` to queue a message as **Next**, which waits for current work to finish. Plain `Enter` continues to send as Steer. Failed submissions retain their mode, attachments, settings, and idempotency key when retried with `Ctrl+R`. Remy can replay sessions containing queued-message dispatch, withdrawal, and failure events. Messages withdrawn or failed before reaching Remy show an activity notice; an active submission ends as cancelled or failed instead of waiting for a turn that never started.
|
|
70
72
|
|
|
71
|
-
Use Up/Down to select a session in the dashboard, Left/Right to load the adjacent page, and `Enter` to open it. Remy
|
|
73
|
+
Use Up/Down to select a session in the dashboard, Left/Right to load the adjacent page, and `Enter` to open it. Remy shows the session header and composer immediately after session details load, while retained conversation history loads in the background. You can start drafting immediately; sending is paused until history finishes, and your draft stays intact. New activity then streams as it arrives. Press `s` to fuzzy-search all accessible sessions through the API by author, repository, branch name, pull request title/number, or session number/ID/title; `Enter` applies immediately and keeps the search active; `Esc` clears it. Typing refreshes results after a short pause. Matching ignores case and query whitespace and finds characters in order within a field (for example, `grch` matches `Grace Hopper`); numbers also accept `#`. Search combines with your other filters and stays active across pages. Session rows and compact cards show the repository and session work branch (`Branch pending` when unassigned). Press `f` to filter sessions by lifecycle status, `a` by author, or `r` by repository. These pickers use checkboxes: Up/Down or `j`/`k` moves, `Space` toggles, Right/`l` checks, Left/`h` clears, `G` and `gg` jump to the last and first option, and `Enter` applies every checked value. An empty picker means all values. Press `s` inside a picker to fuzzy-search its options; `Enter` keeps the matching list and `Esc` cancels the search. Press `Esc` outside search to discard staged checkbox changes. Press `b` to filter by a case-insensitive part of a branch name; Enter applies it, an empty value clears it, and Esc discards edits. Repository and branch filters must match the same repository in a session. Filters apply to the complete remote result set and reset pagination to its first page. After the dashboard list or repository-review list has focus, Vim keys work too: `j`/`k` move, `h`/`l` go to the previous/next dashboard page (or clear/mark a repository), `G` goes to the last row, and `gg` goes to the first row. Press `Shift+L` in the dashboard to start a logout confirmation. In any message composer, plain `Enter` and `Shift+Enter` submit; `Option+Enter` on macOS (`Alt+Enter` elsewhere) inserts a newline. `Ctrl+J` also inserts a newline for terminals that do not report the Option/Alt modifier. `Cmd+Left`/`Cmd+Right` move to the start or end of the current line, as do `Home`/`End`, which many macOS terminals send for those keys; `Ctrl+D` deletes the character after the cursor. In the session view, `Ctrl+C` first clears a non-empty draft; press it again with an empty composer to exit. The conversation header keeps the numbered session title on its first line and repository, pull request, work state, elapsed time, connection state, and the agent's current `Harness · Model · Effort` on its second line. The header also keeps the last provider-reported context observation across reconnects, such as `Context 50% left · 62,000 used`; when the provider does not report a context window, it shows only used tokens. This is an observation, not a live occupancy guarantee or billing total.
|
|
72
74
|
|
|
73
75
|
If a connected session loses its activity stream, Remy retries for up to two minutes and resumes from the last received event. After the live stream stays connected for at least 30 seconds, the next disconnect starts a fresh retry window. Connections that drop sooner share the existing window and backoff. While a submitted message is still awaiting its outcome, retries continue beyond that window.
|
|
74
76
|
|
|
@@ -98,7 +100,7 @@ Command and attachment feedback share progress, success, failure, and attention
|
|
|
98
100
|
|
|
99
101
|
Enter `/compact` to admit a durable main-thread context compaction control without sending a human message. Remy reports request admission immediately and shows the eventual completed, no-op, failed, or unknown provider result in retained activity. Compaction waits for a safe point and does not interrupt active work or jump ahead of an earlier Next message. While admission is pending, you can keep editing or open other commands; repeated `/compact` invocations do not send another request.
|
|
100
102
|
|
|
101
|
-
Drag to select visible text in any Remy view; Remy copies it to the local clipboard and emits OSC 52 for terminal or remote-session clipboard support, then clears the selection highlight. Published file artifacts show their filename and muted, full web preview URL; open the URL in a browser to select that artifact in its session. Tela Page rows retain their canonical Page URL. Downloads are explicit through `/files`; artifact bytes are never printed in the terminal.
|
|
103
|
+
Drag to select visible text in any Remy view; Remy copies it to the local clipboard and emits OSC 52 for terminal or remote-session clipboard support, then clears the selection highlight. Published file artifacts show their filename and muted, full web preview URL; open the URL in a browser to select that artifact in its session. Tela Page rows retain their canonical Page URL. Downloads are explicit through `/files`; artifact bytes are never printed in the terminal. Clicking the conversation keeps keyboard focus in the composer. Mouse scrolling and drag-to-select still work. Code blocks and expanded code/output use colored text on a distinct background without copyable gutter bars or added line indentation.
|
|
102
104
|
|
|
103
105
|
| Control | Result |
|
|
104
106
|
| --- | --- |
|
|
@@ -129,7 +131,7 @@ After a session becomes terminal, press `Esc` to return to the dashboard or `n`
|
|
|
129
131
|
|
|
130
132
|
`Shift+L` in the dashboard and `/logout` in a session ask for confirmation; press `y` to sign out or `Esc` to cancel. In the dashboard and repository-review list, Vim navigation is also available: `j`/`k` move, `h`/`l` change dashboard pages (or clear/mark a repository), `G` goes to the last row, and `gg` goes to the first row.
|
|
131
133
|
|
|
132
|
-
Drag to select visible text in any Remy view; Remy copies it to the local clipboard and emits OSC 52 for terminal or remote-session clipboard support, then clears the selection highlight.
|
|
134
|
+
Drag to select visible text in any Remy view; Remy copies it to the local clipboard and emits OSC 52 for terminal or remote-session clipboard support, then clears the selection highlight. Clicking the conversation keeps keyboard focus in the composer. Mouse scrolling and drag-to-select still work. Code blocks and expanded code/output use colored text on a distinct background without copyable gutter bars or added line indentation.
|
|
133
135
|
|
|
134
136
|
</details>
|
|
135
137
|
|
package/dist/remy.js
CHANGED
|
@@ -33544,7 +33544,8 @@ var sessionDetailResponseSchema = exports_external2.object({
|
|
|
33544
33544
|
}).passthrough()),
|
|
33545
33545
|
repositories: exports_external2.array(exports_external2.strictObject({
|
|
33546
33546
|
id: exports_external2.string(),
|
|
33547
|
-
full_name: exports_external2.string()
|
|
33547
|
+
full_name: exports_external2.string(),
|
|
33548
|
+
branch_name: exports_external2.string().nullable().optional()
|
|
33548
33549
|
})),
|
|
33549
33550
|
connections: exports_external2.array(sessionConnectionSchema).max(1),
|
|
33550
33551
|
artifacts: exports_external2.array(sessionArtifactSchema),
|
|
@@ -33594,7 +33595,8 @@ var sessionSummarySchema = exports_external2.object({
|
|
|
33594
33595
|
source: sessionSourceSchema,
|
|
33595
33596
|
repositories: exports_external2.array(exports_external2.strictObject({
|
|
33596
33597
|
id: exports_external2.string(),
|
|
33597
|
-
full_name: exports_external2.string()
|
|
33598
|
+
full_name: exports_external2.string(),
|
|
33599
|
+
branch_name: exports_external2.string().nullable().default(null)
|
|
33598
33600
|
})),
|
|
33599
33601
|
created_by: sessionCreatorSchema,
|
|
33600
33602
|
created_at: exports_external2.string(),
|
|
@@ -33747,6 +33749,9 @@ async function listSessions({
|
|
|
33747
33749
|
after,
|
|
33748
33750
|
statuses,
|
|
33749
33751
|
creatorUserIds,
|
|
33752
|
+
repositoryIds,
|
|
33753
|
+
branchName,
|
|
33754
|
+
search,
|
|
33750
33755
|
signal
|
|
33751
33756
|
}) {
|
|
33752
33757
|
const searchParams = new URLSearchParams;
|
|
@@ -33758,6 +33763,12 @@ async function listSessions({
|
|
|
33758
33763
|
searchParams.append("status", status);
|
|
33759
33764
|
for (const creatorUserId of creatorUserIds ?? [])
|
|
33760
33765
|
searchParams.append("creator_user_id", creatorUserId);
|
|
33766
|
+
for (const repositoryId of repositoryIds ?? [])
|
|
33767
|
+
searchParams.append("repository_id", repositoryId);
|
|
33768
|
+
if (branchName)
|
|
33769
|
+
searchParams.set("branch_name", branchName);
|
|
33770
|
+
if (search)
|
|
33771
|
+
searchParams.set("search", search);
|
|
33761
33772
|
const suffix = searchParams.size > 0 ? `?${searchParams.toString()}` : "";
|
|
33762
33773
|
const response = await client.request(`/v1/sessions${suffix}`, { signal });
|
|
33763
33774
|
const parsed = sessionListResponseSchema.safeParse(await parseJson2(response, "Session list response was not valid JSON."));
|
|
@@ -33777,7 +33788,7 @@ async function listSessions({
|
|
|
33777
33788
|
draft: pullRequest.draft
|
|
33778
33789
|
})),
|
|
33779
33790
|
source: sessionSourceLabel(session.source),
|
|
33780
|
-
repositories: session.repositories.map((repository) => ({ id: repository.id, fullName: repository.full_name })),
|
|
33791
|
+
repositories: session.repositories.map((repository) => ({ id: repository.id, fullName: repository.full_name, branchName: repository.branch_name })),
|
|
33781
33792
|
creator: sessionCreatorLabel(session.created_by),
|
|
33782
33793
|
createdAt: session.created_at,
|
|
33783
33794
|
updatedAt: session.updated_at
|
|
@@ -34592,17 +34603,23 @@ function formatSessionJson({
|
|
|
34592
34603
|
`;
|
|
34593
34604
|
}
|
|
34594
34605
|
// src/version.ts
|
|
34595
|
-
var remyCliVersion = "1.
|
|
34606
|
+
var remyCliVersion = "1.26.0-rc.0";
|
|
34596
34607
|
|
|
34597
34608
|
// src/self-update.ts
|
|
34598
34609
|
import { spawn } from "child_process";
|
|
34599
34610
|
import { realpath } from "fs/promises";
|
|
34600
|
-
import { join as join3,
|
|
34611
|
+
import { join as join3, isAbsolute } from "path";
|
|
34601
34612
|
import { createInterface } from "readline/promises";
|
|
34602
34613
|
var {semver } = globalThis.Bun;
|
|
34603
34614
|
var packageName = "@meistrari/remy-cli";
|
|
34604
34615
|
var versionSchema = exports_external2.string().regex(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-rc\.(0|[1-9]\d*))?$/);
|
|
34605
34616
|
var tagsSchema = exports_external2.object({ latest: versionSchema, rc: versionSchema.optional() });
|
|
34617
|
+
var miseInstallationsSchema = exports_external2.array(exports_external2.object({
|
|
34618
|
+
install_path: exports_external2.string(),
|
|
34619
|
+
active: exports_external2.boolean(),
|
|
34620
|
+
installed: exports_external2.boolean(),
|
|
34621
|
+
source: exports_external2.object({ type: exports_external2.string(), path: exports_external2.string() }).optional()
|
|
34622
|
+
}));
|
|
34606
34623
|
function createCliUpdates({
|
|
34607
34624
|
currentVersion,
|
|
34608
34625
|
entryPath,
|
|
@@ -34650,11 +34667,26 @@ function createCliUpdates({
|
|
|
34650
34667
|
};
|
|
34651
34668
|
let argv;
|
|
34652
34669
|
let updatedEntry = runningEntry;
|
|
34653
|
-
const miseRoot = await probe(["mise", "where", `npm:${packageName}
|
|
34654
|
-
|
|
34655
|
-
|
|
34670
|
+
const miseRoot = await probe(["mise", "where", `npm:${packageName}`]);
|
|
34671
|
+
let miseRelative = "";
|
|
34672
|
+
if (miseRoot) {
|
|
34673
|
+
for (const layout of ["node_modules", "lib/node_modules"]) {
|
|
34674
|
+
const candidate = join3(layout, packageName, "dist", "remy.js");
|
|
34675
|
+
if (await matchesEntry(join3(miseRoot, candidate))) {
|
|
34676
|
+
miseRelative = candidate;
|
|
34677
|
+
break;
|
|
34678
|
+
}
|
|
34679
|
+
}
|
|
34680
|
+
}
|
|
34681
|
+
const usesMise = miseRelative !== "";
|
|
34656
34682
|
if (usesMise) {
|
|
34657
|
-
|
|
34683
|
+
const lookup = await probe(["mise", "ls", `npm:${packageName}`, "--current", "--json"]);
|
|
34684
|
+
const installations = lookup ? miseInstallationsSchema.parse(JSON.parse(lookup)) : [];
|
|
34685
|
+
const owners = installations.filter((installation) => installation.active && installation.installed && installation.install_path === miseRoot);
|
|
34686
|
+
const source = owners.length === 1 ? owners[0]?.source : undefined;
|
|
34687
|
+
if (!source || !["mise.toml", ".tool-versions"].includes(source.type) || !isAbsolute(source.path))
|
|
34688
|
+
throw new Error("Cannot identify the owning mise config for this Remy installation. Update it with mise directly, then retry remy --version.");
|
|
34689
|
+
argv = ["mise", "use", "--path", source.path, "--pin", `npm:${packageName}@${version5}`];
|
|
34658
34690
|
} else {
|
|
34659
34691
|
const bunBin = await probe(["bun", "pm", "bin", "-g"]);
|
|
34660
34692
|
if (bunBin && await matchesEntry(join3(bunBin, "remy"))) {
|
|
@@ -34679,7 +34711,7 @@ function createCliUpdates({
|
|
|
34679
34711
|
if (result.exitCode !== 0)
|
|
34680
34712
|
throw new Error(`Remy update failed: ${argv[0]} exited with code ${result.exitCode}. Resolve the package manager error and retry remy update.`);
|
|
34681
34713
|
if (usesMise) {
|
|
34682
|
-
const located = await runProcess({ argv: ["mise", "where", `npm:${packageName}
|
|
34714
|
+
const located = await runProcess({ argv: ["mise", "where", `npm:${packageName}`], signal, display: false });
|
|
34683
34715
|
if (located.exitCode !== 0 || !located.stdout.trim())
|
|
34684
34716
|
throw new Error("Update finished but the new mise installation could not be located. Check remy --version before retrying.");
|
|
34685
34717
|
updatedEntry = join3(located.stdout.trim(), miseRelative);
|
|
@@ -35071,6 +35103,7 @@ function createSessionViewState({ detail, activeMessageId }) {
|
|
|
35071
35103
|
title: detail.title ?? null,
|
|
35072
35104
|
aggregateStatus: detail.status,
|
|
35073
35105
|
...detail.agentStatus === undefined ? {} : { agentStatus: detail.agentStatus },
|
|
35106
|
+
historyStatus: "ready",
|
|
35074
35107
|
connectionStatus: "connected",
|
|
35075
35108
|
pullRequest: toSessionPullRequest(detail),
|
|
35076
35109
|
agent: toSessionAgentSettings(detail.agent),
|
|
@@ -36390,19 +36423,22 @@ function createRemoteSessionController(dependencies) {
|
|
|
36390
36423
|
ready = new Promise((resolve) => {
|
|
36391
36424
|
resolveReady = resolve;
|
|
36392
36425
|
});
|
|
36426
|
+
state = { ...createSessionViewState({ detail: input.detail, activeMessageId: dependencies.activeMessageId }), historyStatus: "loading" };
|
|
36393
36427
|
const cache = await readSessionCache(cachePath).catch((error93) => {
|
|
36394
36428
|
handleCacheError(error93);
|
|
36395
36429
|
return null;
|
|
36396
36430
|
});
|
|
36397
36431
|
cachedLastRetainedEventId = input.mode === "live" ? input.lastRetainedEventId ?? cache?.lastRetainedEventId : undefined;
|
|
36398
|
-
|
|
36399
|
-
|
|
36400
|
-
|
|
36401
|
-
});
|
|
36432
|
+
if (stopped)
|
|
36433
|
+
return;
|
|
36434
|
+
state = { ...getState(), activeMessageId: dependencies.activeMessageId ?? cache?.activeMessageId };
|
|
36402
36435
|
if (input.mode === "cold-resume") {
|
|
36403
36436
|
const hydrationGeneration = beginReasoningAttempt();
|
|
36404
36437
|
await hydrateRetainedHistoryFromBeginning(hydrationGeneration);
|
|
36405
36438
|
}
|
|
36439
|
+
if (stopped)
|
|
36440
|
+
return;
|
|
36441
|
+
state = { ...getState(), historyStatus: "ready" };
|
|
36406
36442
|
resolveReady();
|
|
36407
36443
|
publishState();
|
|
36408
36444
|
try {
|
|
@@ -37746,7 +37782,7 @@ function createWorkspaceController({ client, sessionId, signal, operations = {},
|
|
|
37746
37782
|
}
|
|
37747
37783
|
return;
|
|
37748
37784
|
}
|
|
37749
|
-
if (selection.kind !== "changes" && selection.repositoryId !== event.repositoryId)
|
|
37785
|
+
if (event.kind !== "turn-ended" && selection.kind !== "changes" && selection.repositoryId !== event.repositoryId)
|
|
37750
37786
|
return;
|
|
37751
37787
|
if (event.kind === "initialized") {
|
|
37752
37788
|
if (initializedRepositories.has(event.repositoryId))
|
|
@@ -37784,10 +37820,10 @@ import { BoxRenderable as BoxRenderable2, TextRenderable, TextareaRenderable } f
|
|
|
37784
37820
|
import { dim as dim2, fg as fg3, StyledText as StyledText4 } from "@opentui/core";
|
|
37785
37821
|
|
|
37786
37822
|
// src/tui/theme.ts
|
|
37787
|
-
import { bold, dim, fg as fg2, stringToStyledText, StyledText as StyledText3 } from "@opentui/core";
|
|
37823
|
+
import { bg as bg2, bold, dim, fg as fg2, stringToStyledText, StyledText as StyledText3 } from "@opentui/core";
|
|
37788
37824
|
|
|
37789
37825
|
// src/tui/markdown.ts
|
|
37790
|
-
import { fg, StyledText as StyledText2, TextAttributes as TextAttributes2 } from "@opentui/core";
|
|
37826
|
+
import { bg, fg, StyledText as StyledText2, TextAttributes as TextAttributes2 } from "@opentui/core";
|
|
37791
37827
|
|
|
37792
37828
|
// ../../node_modules/.bun/marked@17.0.1/node_modules/marked/lib/marked.esm.js
|
|
37793
37829
|
function L() {
|
|
@@ -39254,7 +39290,7 @@ function renderCodeBlock(token, palette) {
|
|
|
39254
39290
|
`)) {
|
|
39255
39291
|
chunks.push(plain(`
|
|
39256
39292
|
`, { color: palette.body, attributes: 0 }));
|
|
39257
|
-
chunks.push(plain(
|
|
39293
|
+
chunks.push(plain(line, { color: palette.code, background: palette.codeBackground, attributes: 0 }));
|
|
39258
39294
|
}
|
|
39259
39295
|
return chunks;
|
|
39260
39296
|
}
|
|
@@ -39347,6 +39383,8 @@ function trimTrailingNewlines(chunks) {
|
|
|
39347
39383
|
}
|
|
39348
39384
|
function plain(text, style) {
|
|
39349
39385
|
const chunk = fg(style.color)(text);
|
|
39386
|
+
if (style.background)
|
|
39387
|
+
chunk.bg = bg(style.background)(text).bg;
|
|
39350
39388
|
if (style.attributes)
|
|
39351
39389
|
chunk.attributes = style.attributes;
|
|
39352
39390
|
if (style.link)
|
|
@@ -39364,6 +39402,7 @@ var PALETTE = {
|
|
|
39364
39402
|
codeLabel: "#64748B",
|
|
39365
39403
|
codeText: "#93C5FD",
|
|
39366
39404
|
codeBar: "#475569",
|
|
39405
|
+
codeBackground: "#20252E",
|
|
39367
39406
|
reasoning: "#C084FC",
|
|
39368
39407
|
progress: "#60A5FA",
|
|
39369
39408
|
tool: "#22D3EE",
|
|
@@ -39519,7 +39558,7 @@ function renderFencedCodeBody(text) {
|
|
|
39519
39558
|
`)) {
|
|
39520
39559
|
chunks.push(...stringToStyledText(`
|
|
39521
39560
|
`).chunks);
|
|
39522
|
-
chunks.push(
|
|
39561
|
+
chunks.push(bg2(PALETTE.codeBackground)(fg2(PALETTE.codeText)(line)));
|
|
39523
39562
|
}
|
|
39524
39563
|
cursor = start + fence.length;
|
|
39525
39564
|
}
|
|
@@ -39539,7 +39578,8 @@ function renderFormattedMessageBody(text, role2) {
|
|
|
39539
39578
|
muted: PALETTE.dimText,
|
|
39540
39579
|
code: PALETTE.codeText,
|
|
39541
39580
|
codeLabel: PALETTE.codeLabel,
|
|
39542
|
-
codeBar: PALETTE.codeBar
|
|
39581
|
+
codeBar: PALETTE.codeBar,
|
|
39582
|
+
codeBackground: PALETTE.codeBackground
|
|
39543
39583
|
});
|
|
39544
39584
|
if (formattedMessages.size >= FORMATTED_MESSAGE_CACHE_LIMIT)
|
|
39545
39585
|
formattedMessages.delete(formattedMessages.keys().next().value);
|
|
@@ -39547,14 +39587,12 @@ function renderFormattedMessageBody(text, role2) {
|
|
|
39547
39587
|
return formatted;
|
|
39548
39588
|
}
|
|
39549
39589
|
function renderCodeSnippet({ text, language = "code", indent = "" }) {
|
|
39550
|
-
const gutter = `${indent}\u2502 `;
|
|
39551
39590
|
return new StyledText3([
|
|
39552
39591
|
...indent ? [dim(fg2(PALETTE.dimText)(indent))] : [],
|
|
39553
39592
|
dim(fg2(PALETTE.codeLabel)(language)),
|
|
39554
39593
|
fg2(PALETTE.codeText)(`
|
|
39555
|
-
|
|
39556
|
-
|
|
39557
|
-
${gutter}`)}`)
|
|
39594
|
+
`),
|
|
39595
|
+
bg2(PALETTE.codeBackground)(fg2(PALETTE.codeText)(text))
|
|
39558
39596
|
]);
|
|
39559
39597
|
}
|
|
39560
39598
|
function formatTime(occurredAt) {
|
|
@@ -39740,12 +39778,12 @@ function renderKeyHints({ hints, width }) {
|
|
|
39740
39778
|
}
|
|
39741
39779
|
|
|
39742
39780
|
// src/tui/selection.ts
|
|
39743
|
-
import { bg, bold as bold2, fg as fg5 } from "@opentui/core";
|
|
39781
|
+
import { bg as bg3, bold as bold2, fg as fg5 } from "@opentui/core";
|
|
39744
39782
|
function selectionLabel({ text, checked }) {
|
|
39745
39783
|
return checked ? bold2(fg5(PALETTE.humanAccent)(text)) : fg5(PALETTE.dimText)(text);
|
|
39746
39784
|
}
|
|
39747
39785
|
function highlightSelection({ chunks, cursor }) {
|
|
39748
|
-
return cursor ? chunks.map((chunk) =>
|
|
39786
|
+
return cursor ? chunks.map((chunk) => bg3(PALETTE.selectionBg)(chunk)) : chunks;
|
|
39749
39787
|
}
|
|
39750
39788
|
|
|
39751
39789
|
// src/tui/renderer.ts
|
|
@@ -40638,7 +40676,9 @@ var lifecycleStatusOptions = [
|
|
|
40638
40676
|
];
|
|
40639
40677
|
async function createDashboardTui({
|
|
40640
40678
|
initialPage,
|
|
40641
|
-
initialFilters = { statuses: [], creators: [] },
|
|
40679
|
+
initialFilters = { statuses: [], creators: [], repositories: [], branchName: "" },
|
|
40680
|
+
repositories: initialRepositories = [],
|
|
40681
|
+
loadRepositories,
|
|
40642
40682
|
authors: initialAuthors = [],
|
|
40643
40683
|
loadAuthors,
|
|
40644
40684
|
loadPage,
|
|
@@ -40649,23 +40689,27 @@ async function createDashboardTui({
|
|
|
40649
40689
|
let keyHandler;
|
|
40650
40690
|
let resizeHandler;
|
|
40651
40691
|
let refreshTimer;
|
|
40692
|
+
let searchTimer;
|
|
40652
40693
|
let contentScrollHandler;
|
|
40653
40694
|
let rendererDestroyed2 = false;
|
|
40654
40695
|
const loadPageAbortController = new AbortController;
|
|
40655
40696
|
try {
|
|
40656
40697
|
let stopPageRefresh = function() {
|
|
40698
|
+
clearTimeout(searchTimer);
|
|
40657
40699
|
if (refreshTimer !== undefined) {
|
|
40658
40700
|
clearInterval(refreshTimer);
|
|
40659
40701
|
refreshTimer = undefined;
|
|
40660
40702
|
}
|
|
40661
40703
|
loadPageAbortController.abort();
|
|
40704
|
+
}, currentOptionState = function() {
|
|
40705
|
+
return filterMode?.kind === "creator" || filterMode?.kind === "repository" ? optionStates[filterMode.kind] : undefined;
|
|
40662
40706
|
}, render = function() {
|
|
40663
40707
|
const contentWidth = Math.max(1, renderer.width - TUI_LAYOUT.dashboardGutter * 2);
|
|
40664
|
-
const sessions =
|
|
40708
|
+
const sessions = page.sessions.map((session) => prioritizeSessionMatches(session, filters, sessionSearchQuery));
|
|
40665
40709
|
const rowsBelow = rowsBelowVisibleWindow({ sessionCount: sessions.length, selectedIndex, height: renderer.height });
|
|
40666
40710
|
const isNarrow = renderer.width < TUI_LAYOUT.dashboardCardBreakpoint;
|
|
40667
40711
|
const selectedSession = filterMode ? undefined : sessions[selectedIndex];
|
|
40668
|
-
const sessionRows = filterMode ? formatFilterMenu({ mode: filterMode, authors, width: contentWidth }) : sessions.length === 0 ? stringToStyledText2(sessionSearchQuery ? "No sessions match the search." : hasActiveFilters(filters) ? "No sessions match the active filters." : "No remote sessions yet.") : isNarrow ? formatSessionCard({
|
|
40712
|
+
const sessionRows = filterMode ? formatFilterMenu({ mode: filterMode, authors, repositories, width: contentWidth }) : sessions.length === 0 ? stringToStyledText2(sessionSearchQuery ? "No sessions match the search." : hasActiveFilters(filters) ? "No sessions match the active filters." : "No remote sessions yet.") : isNarrow ? formatSessionCard({
|
|
40669
40713
|
session: selectedSession,
|
|
40670
40714
|
selectedIndex,
|
|
40671
40715
|
sessionCount: sessions.length,
|
|
@@ -40715,7 +40759,7 @@ async function createDashboardTui({
|
|
|
40715
40759
|
else if (activeSearchComposer === "session")
|
|
40716
40760
|
sessionSearchComposer.render();
|
|
40717
40761
|
separator.content = activeSearchComposer ? "" : renderComposerDivider({ width: contentWidth });
|
|
40718
|
-
footer.content = filterMode ? dashboardFilterFooter({ mode: filterMode, loadingPage,
|
|
40762
|
+
footer.content = filterMode ? dashboardFilterFooter({ mode: filterMode, loadingPage, loadingOptions: currentOptionState()?.loading ?? false, pageError, optionError: currentOptionState()?.error, width: contentWidth }) : dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmationOpen, sessionSearchOpen, width: contentWidth });
|
|
40719
40763
|
renderer.requestRender();
|
|
40720
40764
|
}, finish = function(value) {
|
|
40721
40765
|
if (settled)
|
|
@@ -40730,7 +40774,7 @@ async function createDashboardTui({
|
|
|
40730
40774
|
stopPageRefresh();
|
|
40731
40775
|
rejectDraft(error93);
|
|
40732
40776
|
}, moveSelectionTo = function(index) {
|
|
40733
|
-
const sessions =
|
|
40777
|
+
const sessions = page.sessions;
|
|
40734
40778
|
if (sessions.length === 0)
|
|
40735
40779
|
return;
|
|
40736
40780
|
selectedIndex = Math.max(0, Math.min(index, sessions.length - 1));
|
|
@@ -40738,6 +40782,10 @@ async function createDashboardTui({
|
|
|
40738
40782
|
}, applyFilterSearch = function() {
|
|
40739
40783
|
if (!filterMode?.searching)
|
|
40740
40784
|
return;
|
|
40785
|
+
if (filterMode.kind === "branch") {
|
|
40786
|
+
applyFilters({ ...filters, statuses: filterMode.statuses, creators: filterMode.creators, repositories: filterMode.repositories, branchName: filterMode.query.trim() });
|
|
40787
|
+
return;
|
|
40788
|
+
}
|
|
40741
40789
|
filterMode = { ...filterMode, searching: false, selectedIndex: 0 };
|
|
40742
40790
|
filterSearchComposer.textarea.setText("");
|
|
40743
40791
|
render();
|
|
@@ -40756,6 +40804,7 @@ async function createDashboardTui({
|
|
|
40756
40804
|
return;
|
|
40757
40805
|
sessionSearchOpen = false;
|
|
40758
40806
|
selectedIndex = 0;
|
|
40807
|
+
loadSessionSearch();
|
|
40759
40808
|
render();
|
|
40760
40809
|
}, cancelSessionSearch = function() {
|
|
40761
40810
|
if (!sessionSearchOpen)
|
|
@@ -40764,6 +40813,7 @@ async function createDashboardTui({
|
|
|
40764
40813
|
sessionSearchQuery = "";
|
|
40765
40814
|
selectedIndex = 0;
|
|
40766
40815
|
sessionSearchComposer.textarea.setText("");
|
|
40816
|
+
loadSessionSearch();
|
|
40767
40817
|
render();
|
|
40768
40818
|
}, handleSessionKey = function(key) {
|
|
40769
40819
|
if (logoutConfirmationOpen) {
|
|
@@ -40784,22 +40834,21 @@ async function createDashboardTui({
|
|
|
40784
40834
|
return;
|
|
40785
40835
|
}
|
|
40786
40836
|
if (filterMode) {
|
|
40787
|
-
const options = visibleDashboardFilterOptions({ mode: filterMode, authors });
|
|
40837
|
+
const options = visibleDashboardFilterOptions({ mode: filterMode, authors, repositories });
|
|
40788
40838
|
if (key.name === "escape") {
|
|
40789
40839
|
key.preventDefault();
|
|
40790
|
-
if (filterMode.searching) {
|
|
40840
|
+
if (filterMode.searching && filterMode.kind !== "branch") {
|
|
40791
40841
|
cancelFilterSearch();
|
|
40792
40842
|
return;
|
|
40793
40843
|
}
|
|
40794
40844
|
filterMode = undefined;
|
|
40795
40845
|
pageError = undefined;
|
|
40796
|
-
authorError = undefined;
|
|
40797
40846
|
render();
|
|
40798
40847
|
return;
|
|
40799
40848
|
}
|
|
40800
40849
|
if (filterMode.searching)
|
|
40801
40850
|
return;
|
|
40802
|
-
if (
|
|
40851
|
+
if (currentOptionState()?.loading) {
|
|
40803
40852
|
key.preventDefault();
|
|
40804
40853
|
return;
|
|
40805
40854
|
}
|
|
@@ -40836,7 +40885,7 @@ async function createDashboardTui({
|
|
|
40836
40885
|
if (!option)
|
|
40837
40886
|
return;
|
|
40838
40887
|
const selected = key.name === "space" ? !isDashboardFilterOptionSelected({ mode: filterMode, option }) : key.name === "right" || key.name === "l";
|
|
40839
|
-
filterMode = updateDashboardFilterSelection({ mode: filterMode, option, selected, authors });
|
|
40888
|
+
filterMode = updateDashboardFilterSelection({ mode: filterMode, option, selected, authors, repositories });
|
|
40840
40889
|
render();
|
|
40841
40890
|
return;
|
|
40842
40891
|
}
|
|
@@ -40849,7 +40898,7 @@ async function createDashboardTui({
|
|
|
40849
40898
|
}
|
|
40850
40899
|
if (key.name === "return" || key.name === "enter") {
|
|
40851
40900
|
key.preventDefault();
|
|
40852
|
-
const nextFilters = { statuses: [...filterMode.statuses], creators: [...filterMode.creators] };
|
|
40901
|
+
const nextFilters = { ...filters, statuses: [...filterMode.statuses], creators: [...filterMode.creators], repositories: [...filterMode.repositories], branchName: filterMode.branchName };
|
|
40853
40902
|
applyFilters(nextFilters);
|
|
40854
40903
|
}
|
|
40855
40904
|
return;
|
|
@@ -40882,7 +40931,7 @@ async function createDashboardTui({
|
|
|
40882
40931
|
if (listFocused && (key.name === "G" || key.name === "g" && key.shift)) {
|
|
40883
40932
|
key.preventDefault();
|
|
40884
40933
|
awaitingVimGoToTop = false;
|
|
40885
|
-
moveSelectionTo(
|
|
40934
|
+
moveSelectionTo(page.sessions.length - 1);
|
|
40886
40935
|
return;
|
|
40887
40936
|
}
|
|
40888
40937
|
if (listFocused && key.name === "g") {
|
|
@@ -40907,7 +40956,9 @@ async function createDashboardTui({
|
|
|
40907
40956
|
awaitingVimGoToTop = false;
|
|
40908
40957
|
if (key.name === "return" || key.name === "enter") {
|
|
40909
40958
|
key.preventDefault();
|
|
40910
|
-
|
|
40959
|
+
if (loadingPage || sessionSearchQuery.trim() !== (filters.search ?? ""))
|
|
40960
|
+
return;
|
|
40961
|
+
const session = page.sessions[selectedIndex];
|
|
40911
40962
|
if (session)
|
|
40912
40963
|
finish({ kind: "session", sessionId: session.id });
|
|
40913
40964
|
return;
|
|
@@ -40922,13 +40973,27 @@ async function createDashboardTui({
|
|
|
40922
40973
|
openSessionSearch();
|
|
40923
40974
|
return;
|
|
40924
40975
|
}
|
|
40976
|
+
if (["f", "a", "r", "b"].includes(key.name) && (loadingPage || sessionSearchQuery.trim() !== (filters.search ?? "")))
|
|
40977
|
+
return;
|
|
40925
40978
|
if (key.name === "f") {
|
|
40926
40979
|
key.preventDefault();
|
|
40927
|
-
authorError = undefined;
|
|
40928
40980
|
filterMode = createFilterMode({ kind: "status", filters });
|
|
40929
40981
|
render();
|
|
40930
40982
|
return;
|
|
40931
40983
|
}
|
|
40984
|
+
if (key.name === "r" || key.name === "b") {
|
|
40985
|
+
key.preventDefault();
|
|
40986
|
+
filterMode = createFilterMode({ kind: key.name === "r" ? "repository" : "branch", filters });
|
|
40987
|
+
if (filterMode.kind === "branch") {
|
|
40988
|
+
filterMode.searching = true;
|
|
40989
|
+
filterMode.query = filters.branchName;
|
|
40990
|
+
filterSearchComposer.textarea.setText(filters.branchName);
|
|
40991
|
+
}
|
|
40992
|
+
render();
|
|
40993
|
+
if (key.name === "r")
|
|
40994
|
+
loadRepositoryOptions();
|
|
40995
|
+
return;
|
|
40996
|
+
}
|
|
40932
40997
|
if (key.name === "a") {
|
|
40933
40998
|
key.preventDefault();
|
|
40934
40999
|
openAuthorFilter();
|
|
@@ -40971,17 +41036,22 @@ async function createDashboardTui({
|
|
|
40971
41036
|
let selectedIndex = 0;
|
|
40972
41037
|
let loadingPage = false;
|
|
40973
41038
|
let pageRequestInFlight = false;
|
|
41039
|
+
let pageRequestVersion = 0;
|
|
40974
41040
|
let pageError;
|
|
40975
|
-
|
|
41041
|
+
const optionStates = {
|
|
41042
|
+
creator: { loading: false, error: undefined },
|
|
41043
|
+
repository: { loading: false, error: undefined }
|
|
41044
|
+
};
|
|
41045
|
+
let repositories = initialRepositories;
|
|
41046
|
+
let repositoriesLoaded = loadRepositories === undefined || initialRepositories.length > 0;
|
|
40976
41047
|
let authors = initialAuthors;
|
|
40977
41048
|
let authorsLoaded = loadAuthors === undefined || initialAuthors.length > 0;
|
|
40978
|
-
let loadingAuthors = false;
|
|
40979
41049
|
let awaitingVimGoToTop = false;
|
|
40980
41050
|
let logoutConfirmationOpen = false;
|
|
40981
41051
|
let filters = initialFilters;
|
|
40982
41052
|
let filterMode;
|
|
40983
41053
|
let sessionSearchOpen = false;
|
|
40984
|
-
let sessionSearchQuery = "";
|
|
41054
|
+
let sessionSearchQuery = initialFilters.search ?? "";
|
|
40985
41055
|
root.onMouseDown = (event) => {
|
|
40986
41056
|
if (filterMode?.searching || sessionSearchOpen)
|
|
40987
41057
|
return;
|
|
@@ -41021,7 +41091,7 @@ async function createDashboardTui({
|
|
|
41021
41091
|
renderer,
|
|
41022
41092
|
parent: filterSearchRegion,
|
|
41023
41093
|
tag: "Search",
|
|
41024
|
-
placeholder: "Search
|
|
41094
|
+
placeholder: "Search sessions, authors, repositories, branches, or PRs\u2026",
|
|
41025
41095
|
screenPaddingX: 2,
|
|
41026
41096
|
onSubmit: () => applySessionSearch(),
|
|
41027
41097
|
onContentChange: () => {
|
|
@@ -41029,104 +41099,151 @@ async function createDashboardTui({
|
|
|
41029
41099
|
return;
|
|
41030
41100
|
sessionSearchQuery = sessionSearchComposer.textarea.plainText;
|
|
41031
41101
|
selectedIndex = 0;
|
|
41102
|
+
pageRequestVersion += 1;
|
|
41103
|
+
loadingPage = true;
|
|
41104
|
+
clearTimeout(searchTimer);
|
|
41105
|
+
searchTimer = setTimeout(() => {
|
|
41106
|
+
searchTimer = undefined;
|
|
41107
|
+
loadSessionSearch();
|
|
41108
|
+
}, 200);
|
|
41032
41109
|
render();
|
|
41033
41110
|
}
|
|
41034
41111
|
});
|
|
41035
41112
|
async function loadAdjacentPage(direction) {
|
|
41036
|
-
if (pageRequestInFlight)
|
|
41113
|
+
if (pageRequestInFlight || loadingPage || sessionSearchQuery.trim() !== (filters.search ?? ""))
|
|
41037
41114
|
return;
|
|
41038
41115
|
const canLoad = direction === "next" ? page.hasMore : page.canGoPrevious;
|
|
41039
41116
|
if (!canLoad)
|
|
41040
41117
|
return;
|
|
41118
|
+
const requestVersion = ++pageRequestVersion;
|
|
41041
41119
|
pageRequestInFlight = true;
|
|
41042
41120
|
loadingPage = true;
|
|
41043
41121
|
pageError = undefined;
|
|
41044
41122
|
render();
|
|
41045
41123
|
try {
|
|
41046
41124
|
const loadedPage = await loadPage({ target: direction, signal: loadPageAbortController.signal });
|
|
41047
|
-
if (destroyed || settled)
|
|
41125
|
+
if (destroyed || settled || requestVersion !== pageRequestVersion)
|
|
41048
41126
|
return;
|
|
41049
41127
|
page = loadedPage;
|
|
41050
41128
|
pageIndex = Math.max(1, pageIndex + (direction === "next" ? 1 : -1));
|
|
41051
|
-
const sessions =
|
|
41129
|
+
const sessions = page.sessions;
|
|
41052
41130
|
selectedIndex = direction === "next" ? 0 : Math.max(0, sessions.length - 1);
|
|
41053
41131
|
} catch (error93) {
|
|
41054
|
-
if (!destroyed && !settled)
|
|
41132
|
+
if (!destroyed && !settled && requestVersion === pageRequestVersion)
|
|
41055
41133
|
pageError = error93 instanceof Error ? error93.message : String(error93);
|
|
41056
41134
|
} finally {
|
|
41057
|
-
|
|
41058
|
-
|
|
41059
|
-
|
|
41060
|
-
|
|
41135
|
+
if (requestVersion === pageRequestVersion) {
|
|
41136
|
+
pageRequestInFlight = false;
|
|
41137
|
+
loadingPage = false;
|
|
41138
|
+
if (!destroyed && !settled)
|
|
41139
|
+
render();
|
|
41140
|
+
}
|
|
41061
41141
|
}
|
|
41062
41142
|
}
|
|
41063
41143
|
async function refreshCurrentPage() {
|
|
41064
|
-
if (pageRequestInFlight || destroyed || settled)
|
|
41144
|
+
if (pageRequestInFlight || loadingPage || destroyed || settled || sessionSearchQuery.trim() !== (filters.search ?? ""))
|
|
41065
41145
|
return;
|
|
41146
|
+
const requestVersion = ++pageRequestVersion;
|
|
41066
41147
|
pageRequestInFlight = true;
|
|
41067
41148
|
try {
|
|
41068
41149
|
const refreshedPage = await loadPage({ target: "current", signal: loadPageAbortController.signal });
|
|
41069
|
-
if (destroyed || settled)
|
|
41150
|
+
if (destroyed || settled || requestVersion !== pageRequestVersion)
|
|
41070
41151
|
return;
|
|
41071
|
-
const selectedSessionId =
|
|
41152
|
+
const selectedSessionId = page.sessions[selectedIndex]?.id;
|
|
41072
41153
|
page = refreshedPage;
|
|
41073
|
-
const refreshedSelectedIndex = selectedSessionId ?
|
|
41154
|
+
const refreshedSelectedIndex = selectedSessionId ? page.sessions.findIndex((session) => session.id === selectedSessionId) : -1;
|
|
41074
41155
|
selectedIndex = refreshedSelectedIndex >= 0 ? refreshedSelectedIndex : Math.max(0, Math.min(selectedIndex, page.sessions.length - 1));
|
|
41075
41156
|
pageError = undefined;
|
|
41076
41157
|
render();
|
|
41077
41158
|
} catch (error93) {
|
|
41078
|
-
if (!destroyed && !settled) {
|
|
41159
|
+
if (!destroyed && !settled && requestVersion === pageRequestVersion) {
|
|
41079
41160
|
pageError = error93 instanceof Error ? error93.message : String(error93);
|
|
41080
41161
|
render();
|
|
41081
41162
|
}
|
|
41082
41163
|
} finally {
|
|
41083
|
-
|
|
41164
|
+
if (requestVersion === pageRequestVersion)
|
|
41165
|
+
pageRequestInFlight = false;
|
|
41084
41166
|
}
|
|
41085
41167
|
}
|
|
41086
41168
|
async function applyFilters(nextFilters) {
|
|
41087
|
-
|
|
41088
|
-
|
|
41169
|
+
const appliedFilterMode = filterMode;
|
|
41170
|
+
const requestVersion = ++pageRequestVersion;
|
|
41171
|
+
pageRequestInFlight = true;
|
|
41089
41172
|
loadingPage = true;
|
|
41090
41173
|
pageError = undefined;
|
|
41091
|
-
authorError = undefined;
|
|
41092
41174
|
render();
|
|
41093
41175
|
try {
|
|
41094
|
-
|
|
41176
|
+
const loadedPage = await loadPage({ filters: nextFilters, signal: loadPageAbortController.signal });
|
|
41177
|
+
if (destroyed || settled || requestVersion !== pageRequestVersion)
|
|
41178
|
+
return;
|
|
41179
|
+
page = loadedPage;
|
|
41095
41180
|
filters = nextFilters;
|
|
41096
41181
|
pageIndex = 1;
|
|
41097
41182
|
selectedIndex = 0;
|
|
41098
|
-
filterMode
|
|
41183
|
+
if (filterMode === appliedFilterMode)
|
|
41184
|
+
filterMode = undefined;
|
|
41099
41185
|
} catch (error93) {
|
|
41100
|
-
|
|
41186
|
+
if (!destroyed && !settled && requestVersion === pageRequestVersion)
|
|
41187
|
+
pageError = error93 instanceof Error ? error93.message : String(error93);
|
|
41101
41188
|
} finally {
|
|
41102
|
-
|
|
41103
|
-
|
|
41189
|
+
if (requestVersion === pageRequestVersion) {
|
|
41190
|
+
pageRequestInFlight = false;
|
|
41191
|
+
loadingPage = false;
|
|
41192
|
+
if (!destroyed && !settled)
|
|
41193
|
+
render();
|
|
41194
|
+
}
|
|
41195
|
+
}
|
|
41196
|
+
}
|
|
41197
|
+
async function loadRepositoryOptions() {
|
|
41198
|
+
if (repositoriesLoaded || optionStates.repository.loading || !loadRepositories)
|
|
41199
|
+
return;
|
|
41200
|
+
optionStates.repository.loading = true;
|
|
41201
|
+
optionStates.repository.error = undefined;
|
|
41202
|
+
render();
|
|
41203
|
+
try {
|
|
41204
|
+
const loaded = await loadRepositories();
|
|
41205
|
+
if (destroyed || settled)
|
|
41206
|
+
return;
|
|
41207
|
+
repositories = loaded;
|
|
41208
|
+
repositoriesLoaded = true;
|
|
41209
|
+
} catch (error93) {
|
|
41210
|
+
if (!destroyed && !settled)
|
|
41211
|
+
optionStates.repository.error = error93 instanceof Error ? error93.message : String(error93);
|
|
41212
|
+
} finally {
|
|
41213
|
+
optionStates.repository.loading = false;
|
|
41214
|
+
if (!destroyed && !settled)
|
|
41215
|
+
render();
|
|
41104
41216
|
}
|
|
41105
41217
|
}
|
|
41106
41218
|
async function openAuthorFilter() {
|
|
41107
41219
|
filterMode = createFilterMode({ kind: "creator", filters });
|
|
41108
|
-
|
|
41220
|
+
optionStates.creator.error = undefined;
|
|
41109
41221
|
render();
|
|
41110
|
-
if (authorsLoaded ||
|
|
41222
|
+
if (authorsLoaded || optionStates.creator.loading || !loadAuthors)
|
|
41111
41223
|
return;
|
|
41112
|
-
|
|
41224
|
+
optionStates.creator.loading = true;
|
|
41113
41225
|
render();
|
|
41114
41226
|
try {
|
|
41115
|
-
|
|
41227
|
+
const loaded = await loadAuthors();
|
|
41228
|
+
if (destroyed || settled)
|
|
41229
|
+
return;
|
|
41230
|
+
authors = loaded;
|
|
41116
41231
|
authorsLoaded = true;
|
|
41117
41232
|
if (filterMode?.kind === "creator") {
|
|
41118
41233
|
const firstSelectedIndex = authors.findIndex((author) => filterMode?.creators.some((creator) => creator.id === author.id));
|
|
41119
41234
|
filterMode = { ...filterMode, selectedIndex: Math.max(0, firstSelectedIndex) };
|
|
41120
41235
|
}
|
|
41121
41236
|
} catch (error93) {
|
|
41122
|
-
|
|
41237
|
+
if (!destroyed && !settled)
|
|
41238
|
+
optionStates.creator.error = error93 instanceof Error ? error93.message : String(error93);
|
|
41123
41239
|
} finally {
|
|
41124
|
-
|
|
41125
|
-
|
|
41240
|
+
optionStates.creator.loading = false;
|
|
41241
|
+
if (!destroyed && !settled)
|
|
41242
|
+
render();
|
|
41126
41243
|
}
|
|
41127
41244
|
}
|
|
41128
41245
|
async function moveSelection(direction) {
|
|
41129
|
-
const sessions =
|
|
41246
|
+
const sessions = page.sessions;
|
|
41130
41247
|
if (loadingPage || sessions.length === 0)
|
|
41131
41248
|
return;
|
|
41132
41249
|
const isBoundary = direction === "next" ? selectedIndex === sessions.length - 1 : selectedIndex === 0;
|
|
@@ -41137,6 +41254,12 @@ async function createDashboardTui({
|
|
|
41137
41254
|
}
|
|
41138
41255
|
await loadAdjacentPage(direction);
|
|
41139
41256
|
}
|
|
41257
|
+
async function loadSessionSearch() {
|
|
41258
|
+
clearTimeout(searchTimer);
|
|
41259
|
+
searchTimer = undefined;
|
|
41260
|
+
const search = sessionSearchQuery.trim();
|
|
41261
|
+
await applyFilters({ ...filters, search: search || undefined });
|
|
41262
|
+
}
|
|
41140
41263
|
inputHandler = (sequence) => {
|
|
41141
41264
|
if (sequence === "\x03") {
|
|
41142
41265
|
finish({ kind: "interrupt" });
|
|
@@ -41215,7 +41338,8 @@ function formatSessionRow({ session, selected, width }) {
|
|
|
41215
41338
|
const title = truncateText(session.title ?? "Untitled session", Math.max(8, Math.min(40, Math.floor(width * 0.24))));
|
|
41216
41339
|
const attribution = `${session.creator} (${session.source})`;
|
|
41217
41340
|
const metadataWidth = Math.max(0, width - Bun.stringWidth(prefix + title + activity.label) - 4);
|
|
41218
|
-
const
|
|
41341
|
+
const work = session.repositories.length > 0 ? `${sessionRepositoryBranches(session, Math.max(0, Math.floor(metadataWidth * 0.6)))} \xB7 ` : "";
|
|
41342
|
+
const metadata = truncateText(`${work}${attribution} \xB7 ${outcome} \xB7 ${updated}`, metadataWidth);
|
|
41219
41343
|
const line = [
|
|
41220
41344
|
fg7(PALETTE.dimText)(prefix),
|
|
41221
41345
|
fg7(PALETTE.bodyText)(title),
|
|
@@ -41243,14 +41367,18 @@ function createFilterMode({ kind, filters }) {
|
|
|
41243
41367
|
selectedIndex: 0,
|
|
41244
41368
|
statuses: [...filters.statuses],
|
|
41245
41369
|
creators: [...filters.creators],
|
|
41370
|
+
repositories: [...filters.repositories],
|
|
41371
|
+
branchName: filters.branchName,
|
|
41246
41372
|
query: "",
|
|
41247
41373
|
searching: false
|
|
41248
41374
|
};
|
|
41249
41375
|
}
|
|
41250
|
-
function formatFilterMenu({ mode, authors, width }) {
|
|
41251
|
-
|
|
41252
|
-
|
|
41253
|
-
const
|
|
41376
|
+
function formatFilterMenu({ mode, authors, repositories, width }) {
|
|
41377
|
+
if (mode.kind === "branch")
|
|
41378
|
+
return stringToStyledText2("Filter by branch \xB7 contains text \xB7 empty shows all");
|
|
41379
|
+
const options = visibleDashboardFilterOptions({ mode, authors, repositories });
|
|
41380
|
+
const selectedCount = mode.kind === "status" ? mode.statuses.length : mode.kind === "repository" ? mode.repositories.length : mode.creators.length;
|
|
41381
|
+
const heading = `${mode.kind === "status" ? "Filter by status" : mode.kind === "repository" ? "Filter by repository" : "Filter by author"} \xB7 ${selectedCount} selected \xB7 empty shows all`;
|
|
41254
41382
|
const rows = options.map((option, index) => {
|
|
41255
41383
|
const active = isDashboardFilterOptionSelected({ mode, option });
|
|
41256
41384
|
const label = `${active ? "[x]" : "[ ]"} ${truncateText(option.label, Math.max(1, width - 4))}`;
|
|
@@ -41265,16 +41393,16 @@ function formatFilterMenu({ mode, authors, width }) {
|
|
|
41265
41393
|
], `
|
|
41266
41394
|
`);
|
|
41267
41395
|
}
|
|
41268
|
-
function dashboardFilterOptions({ kind, authors }) {
|
|
41269
|
-
return kind === "status" ? lifecycleStatusOptions.map((option) => ({ kind: "status", id: option.value, label: option.label })) : authors.map((author) => ({ kind: "creator", id: author.id, label: author.name }));
|
|
41396
|
+
function dashboardFilterOptions({ kind, authors, repositories }) {
|
|
41397
|
+
return kind === "status" ? lifecycleStatusOptions.map((option) => ({ kind: "status", id: option.value, label: option.label })) : kind === "repository" ? repositories.map((repository) => ({ kind: "repository", id: repository.id, label: repository.fullName })) : authors.map((author) => ({ kind: "creator", id: author.id, label: author.name }));
|
|
41270
41398
|
}
|
|
41271
|
-
function visibleDashboardFilterOptions({ mode, authors }) {
|
|
41272
|
-
return dashboardFilterOptions({ kind: mode.kind, authors }).filter((option) => fuzzyMatches(option.label, mode.query));
|
|
41399
|
+
function visibleDashboardFilterOptions({ mode, authors, repositories }) {
|
|
41400
|
+
return dashboardFilterOptions({ kind: mode.kind, authors, repositories }).filter((option) => fuzzyMatches(option.label, mode.query));
|
|
41273
41401
|
}
|
|
41274
41402
|
function isDashboardFilterOptionSelected({ mode, option }) {
|
|
41275
|
-
return option.kind === "status" ? mode.statuses.includes(option.id) : mode.creators.some((creator) => creator.id === option.id);
|
|
41403
|
+
return option.kind === "status" ? mode.statuses.includes(option.id) : option.kind === "repository" ? mode.repositories.some((repository) => repository.id === option.id) : mode.creators.some((creator) => creator.id === option.id);
|
|
41276
41404
|
}
|
|
41277
|
-
function updateDashboardFilterSelection({ mode, option, selected, authors }) {
|
|
41405
|
+
function updateDashboardFilterSelection({ mode, option, selected, authors, repositories }) {
|
|
41278
41406
|
if (option.kind === "status") {
|
|
41279
41407
|
const selectedStatuses = new Set(mode.statuses);
|
|
41280
41408
|
if (selected)
|
|
@@ -41286,6 +41414,14 @@ function updateDashboardFilterSelection({ mode, option, selected, authors }) {
|
|
|
41286
41414
|
statuses: lifecycleStatusOptions.map((entry2) => entry2.value).filter((status) => selectedStatuses.has(status))
|
|
41287
41415
|
};
|
|
41288
41416
|
}
|
|
41417
|
+
if (option.kind === "repository") {
|
|
41418
|
+
const ids = new Set(mode.repositories.map((repository) => repository.id));
|
|
41419
|
+
if (selected)
|
|
41420
|
+
ids.add(option.id);
|
|
41421
|
+
else
|
|
41422
|
+
ids.delete(option.id);
|
|
41423
|
+
return { ...mode, repositories: repositories.filter((repository) => ids.has(repository.id)).map(({ id, fullName }) => ({ id, fullName })) };
|
|
41424
|
+
}
|
|
41289
41425
|
const selectedCreatorIds = new Set(mode.creators.map((creator) => creator.id));
|
|
41290
41426
|
if (selected)
|
|
41291
41427
|
selectedCreatorIds.add(option.id);
|
|
@@ -41313,7 +41449,7 @@ function formatSessionPreview({ session, width }) {
|
|
|
41313
41449
|
fg7(PALETTE.dimText)(" \xB7 "),
|
|
41314
41450
|
bold3(fg7(activity.color)(activity.label))
|
|
41315
41451
|
],
|
|
41316
|
-
[fg7(PALETTE.dimText)(truncateText(`Work: ${sessionWork(session)}`, width))],
|
|
41452
|
+
[fg7(PALETTE.dimText)(truncateText(`Work: ${sessionWork(session, width)}`, width))],
|
|
41317
41453
|
[fg7(PALETTE.bodyText)(truncateText(`Outcome: ${sessionOutcome(session)}`, width))],
|
|
41318
41454
|
[fg7(PALETTE.dimText)(truncateText(`Created by: ${session.creator} \xB7 Updated ${formatUpdatedAt(session.updatedAt ?? session.createdAt)}`, width))]
|
|
41319
41455
|
], `
|
|
@@ -41324,7 +41460,7 @@ function formatSessionCard({ session, selectedIndex, sessionCount, width }) {
|
|
|
41324
41460
|
const lines = [
|
|
41325
41461
|
`Session #${session.sessionNumber} \xB7 ${selectedIndex + 1} of ${sessionCount}`,
|
|
41326
41462
|
truncateText(session.title ?? "Untitled session", width),
|
|
41327
|
-
truncateText(`Work: ${sessionWork(session)}`, width),
|
|
41463
|
+
truncateText(`Work: ${sessionWork(session, width)}`, width),
|
|
41328
41464
|
`Now: ${activity.label}`,
|
|
41329
41465
|
truncateText(`Outcome: ${sessionOutcome(session)}`, width),
|
|
41330
41466
|
truncateText(`Created by: ${session.creator} \xB7 Updated ${formatUpdatedAt(session.updatedAt ?? session.createdAt)}`, width)
|
|
@@ -41357,9 +41493,33 @@ function sessionActivityPresentation(session) {
|
|
|
41357
41493
|
return { ...agent, label: "Ready" };
|
|
41358
41494
|
return agent;
|
|
41359
41495
|
}
|
|
41360
|
-
function
|
|
41361
|
-
const
|
|
41362
|
-
|
|
41496
|
+
function prioritizeSessionMatches(session, filters, query) {
|
|
41497
|
+
const filteredRepositories = session.repositories.filter((repository2) => (filters.repositories.length === 0 || filters.repositories.some((selected) => selected.id === repository2.id)) && (!filters.branchName || repository2.branchName?.toLowerCase().includes(filters.branchName.toLowerCase())));
|
|
41498
|
+
const repository = filteredRepositories.find((repository2) => fuzzyMatches(repository2.fullName, query) || fuzzyMatches(repository2.branchName ?? "", query)) ?? filteredRepositories[0];
|
|
41499
|
+
const pullRequest = session.pullRequests.find((pullRequest2) => matchesPullRequestSearch(pullRequest2, query));
|
|
41500
|
+
return {
|
|
41501
|
+
...session,
|
|
41502
|
+
repositories: repository ? [repository, ...session.repositories.filter((item) => item !== repository)] : session.repositories,
|
|
41503
|
+
pullRequests: pullRequest ? [pullRequest, ...session.pullRequests.filter((item) => item !== pullRequest)] : session.pullRequests
|
|
41504
|
+
};
|
|
41505
|
+
}
|
|
41506
|
+
function matchesPullRequestSearch(pullRequest, query) {
|
|
41507
|
+
return fuzzyMatches(`#${pullRequest.number}`, query) || fuzzyMatches(pullRequest.title, query);
|
|
41508
|
+
}
|
|
41509
|
+
function sessionRepositoryBranches(session, width) {
|
|
41510
|
+
const repository = session.repositories[0];
|
|
41511
|
+
if (!repository)
|
|
41512
|
+
return truncateText("No repository selected", width);
|
|
41513
|
+
const more = session.repositories.length > 1 ? ` +${session.repositories.length - 1}` : "";
|
|
41514
|
+
const available = Math.max(0, width - Bun.stringWidth(more) - 3);
|
|
41515
|
+
const name = truncateText(repository.fullName, Math.floor(available / 2));
|
|
41516
|
+
const branch = truncateText(repository.branchName ?? "Branch pending", Math.max(0, available - Bun.stringWidth(name)));
|
|
41517
|
+
return `${name} \xB7 ${branch}${more}`;
|
|
41518
|
+
}
|
|
41519
|
+
function sessionWork(session, width) {
|
|
41520
|
+
const source = truncateText(session.source, Math.floor(width / 4));
|
|
41521
|
+
const repositories = sessionRepositoryBranches(session, Math.max(0, width - Bun.stringWidth(source) - 9));
|
|
41522
|
+
return `${repositories} \xB7 ${source}`;
|
|
41363
41523
|
}
|
|
41364
41524
|
function sessionOutcome(session) {
|
|
41365
41525
|
const first = session.pullRequests[0];
|
|
@@ -41382,14 +41542,8 @@ function formatUpdatedAt(value) {
|
|
|
41382
41542
|
return `${Math.floor(seconds / (60 * 60))}h ago`;
|
|
41383
41543
|
return new Date(milliseconds).toLocaleDateString(undefined, { month: "short", day: "numeric" });
|
|
41384
41544
|
}
|
|
41385
|
-
function matchesSessionSearch({ session, query }) {
|
|
41386
|
-
return fuzzyMatches(session.id, query) || fuzzyMatches(session.title ?? "", query) || fuzzyMatches(String(session.sessionNumber), query);
|
|
41387
|
-
}
|
|
41388
|
-
function visibleDashboardSessions({ sessions, query }) {
|
|
41389
|
-
return sessions.filter((session) => matchesSessionSearch({ session, query }));
|
|
41390
|
-
}
|
|
41391
41545
|
function dashboardStatusBand({ page, pageIndex, filters, sessionSearchQuery }) {
|
|
41392
|
-
const openCount =
|
|
41546
|
+
const openCount = page.sessions.filter((session) => session.status === "open").length;
|
|
41393
41547
|
const paging = [`page ${pageIndex}`];
|
|
41394
41548
|
if (page.canGoPrevious)
|
|
41395
41549
|
paging.push("\u2190 prev");
|
|
@@ -41398,12 +41552,14 @@ function dashboardStatusBand({ page, pageIndex, filters, sessionSearchQuery }) {
|
|
|
41398
41552
|
const activeFilters = [
|
|
41399
41553
|
filters.statuses.length > 0 ? `status: ${filters.statuses.map(sessionStatusLabel).join(", ")}` : undefined,
|
|
41400
41554
|
filters.creators.length > 0 ? `author: ${filters.creators.map((creator) => creator.name).join(", ")}` : undefined,
|
|
41555
|
+
filters.repositories.length > 0 ? `repo: ${filters.repositories.map((repository) => repository.fullName).join(", ")}` : undefined,
|
|
41556
|
+
filters.branchName ? `branch: ${filters.branchName}` : undefined,
|
|
41401
41557
|
sessionSearchQuery ? `search: ${sessionSearchQuery}` : undefined
|
|
41402
41558
|
].filter((value) => value !== undefined);
|
|
41403
41559
|
return `remy \xB7 sessions \xB7 ${openCount} open${activeFilters.length > 0 ? ` \xB7 ${activeFilters.join(" \xB7 ")}` : ""} \xB7 ${paging.join(" \xB7 ")}`;
|
|
41404
41560
|
}
|
|
41405
41561
|
function hasActiveFilters(filters) {
|
|
41406
|
-
return filters.statuses.length > 0 || filters.creators.length > 0;
|
|
41562
|
+
return filters.statuses.length > 0 || filters.creators.length > 0 || filters.repositories.length > 0 || filters.branchName.length > 0;
|
|
41407
41563
|
}
|
|
41408
41564
|
function dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmationOpen, sessionSearchOpen, width }) {
|
|
41409
41565
|
if (logoutConfirmationOpen)
|
|
@@ -41419,19 +41575,24 @@ function dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmation
|
|
|
41419
41575
|
parts.push(`\u2193 ${rowsBelow} more below`);
|
|
41420
41576
|
return joinStyled([
|
|
41421
41577
|
...parts.length > 0 ? [[fg7(pageError ? PALETTE.failure : PALETTE.dimText)(parts.join(" \xB7 "))]] : [],
|
|
41422
|
-
renderKeyHints({ hints: [{ key: "\u2191\u2193", action: "move" }, { key: "\u23CE", action: "open" }, { key: "s", action: "search" }, { key: "f", action: "status" }, { key: "a", action: "author" }, { key: "n", action: "new session" }, { key: "\u21E7l", action: "log out" }, { key: "q", action: "quit" }], width })
|
|
41578
|
+
renderKeyHints({ hints: [{ key: "\u2191\u2193", action: "move" }, { key: "\u23CE", action: "open" }, { key: "s", action: "search" }, { key: "f", action: "status" }, { key: "a", action: "author" }, { key: "r", action: "repository" }, { key: "b", action: "branch" }, { key: "n", action: "new session" }, { key: "\u21E7l", action: "log out" }, { key: "q", action: "quit" }], width })
|
|
41423
41579
|
], `
|
|
41424
41580
|
`);
|
|
41425
41581
|
}
|
|
41426
|
-
function dashboardFilterFooter({ mode, loadingPage,
|
|
41427
|
-
if (
|
|
41428
|
-
return joinStyled([[fg7(PALETTE.progress)("Loading
|
|
41582
|
+
function dashboardFilterFooter({ mode, loadingPage, loadingOptions, pageError, optionError, width }) {
|
|
41583
|
+
if (loadingOptions)
|
|
41584
|
+
return joinStyled([[fg7(PALETTE.progress)("Loading filter options\u2026")], renderKeyHints({ hints: [{ key: "Esc", action: "cancel" }], width })], " \xB7 ");
|
|
41429
41585
|
if (loadingPage)
|
|
41430
41586
|
return stringToStyledText2("Applying filter\u2026");
|
|
41431
|
-
if (mode.searching)
|
|
41432
|
-
return
|
|
41587
|
+
if (mode.searching) {
|
|
41588
|
+
return joinStyled([
|
|
41589
|
+
...pageError ? [[fg7(PALETTE.failure)(`Could not apply filter: ${pageError}`)]] : [],
|
|
41590
|
+
renderKeyHints({ hints: [{ key: "type", action: "to filter" }, { key: "\u23CE", action: mode.kind === "branch" ? "apply branch" : "apply search" }, { key: "Esc", action: "cancel" }], width })
|
|
41591
|
+
], `
|
|
41592
|
+
`);
|
|
41593
|
+
}
|
|
41433
41594
|
return joinStyled([
|
|
41434
|
-
...
|
|
41595
|
+
...optionError ? [[fg7(PALETTE.failure)(`Could not load filter options: ${optionError}`)]] : [],
|
|
41435
41596
|
...pageError ? [[fg7(PALETTE.failure)(`Could not apply filter: ${pageError}`)]] : [],
|
|
41436
41597
|
renderKeyHints({ hints: [{ key: "\u2191\u2193/jk", action: "move" }, { key: "space", action: "toggle" }, { key: "\u2190\u2192/hl", action: "mark" }, { key: "s", action: "search" }, { key: "\u23CE", action: "apply" }, { key: "Esc", action: "cancel" }], width })
|
|
41437
41598
|
], `
|
|
@@ -41442,7 +41603,7 @@ async function createDefaultRenderer() {
|
|
|
41442
41603
|
}
|
|
41443
41604
|
|
|
41444
41605
|
// src/tui/new-session-wizard.ts
|
|
41445
|
-
import { bg as
|
|
41606
|
+
import { bg as bg4, bold as bold4, CliRenderEvents as CliRenderEvents4, dim as dim6, fg as fg11, StyledText as StyledText10, stringToStyledText as stringToStyledText3, TextRenderable as TextRenderable4 } from "@opentui/core";
|
|
41446
41607
|
|
|
41447
41608
|
// src/tui/attachment-presentation.ts
|
|
41448
41609
|
import { dim as dim4, fg as fg9, StyledText as StyledText8 } from "@opentui/core";
|
|
@@ -41492,7 +41653,7 @@ var SPINNER_INTERVAL_MS = 80;
|
|
|
41492
41653
|
// src/tui/path-completion.ts
|
|
41493
41654
|
import { readdir } from "fs/promises";
|
|
41494
41655
|
import { homedir } from "os";
|
|
41495
|
-
import { basename as basename5, dirname as dirname5, isAbsolute as isAbsolute2, join as join8, relative
|
|
41656
|
+
import { basename as basename5, dirname as dirname5, isAbsolute as isAbsolute2, join as join8, relative, resolve as resolve2, sep } from "path";
|
|
41496
41657
|
var maximumCandidates = 50;
|
|
41497
41658
|
async function completePathToken({ text, cwd, homeDirectory = homedir() }) {
|
|
41498
41659
|
const token = activePathToken(text);
|
|
@@ -41512,7 +41673,7 @@ async function completePathToken({ text, cwd, homeDirectory = homedir() }) {
|
|
|
41512
41673
|
}
|
|
41513
41674
|
return entries.filter((entry2) => entry2.name.toLowerCase().startsWith(prefix.toLowerCase())).sort((left, right) => left.name.localeCompare(right.name)).slice(0, maximumCandidates).map((entry2) => {
|
|
41514
41675
|
const completedPath = join8(directory, entry2.name);
|
|
41515
|
-
const replacementPath = pathUsesTilde ? `~/${
|
|
41676
|
+
const replacementPath = pathUsesTilde ? `~/${relative(homeDirectory, completedPath)}` : isAbsolute2(pathText) ? completedPath : relative(cwd, completedPath) || entry2.name;
|
|
41516
41677
|
const normalized = replacementPath.split(sep).join("/");
|
|
41517
41678
|
const suffix = entry2.isDirectory() ? "/" : "";
|
|
41518
41679
|
const completed = `${normalized}${suffix}`;
|
|
@@ -42920,7 +43081,7 @@ function renderStepIndicator({ step, hasInstallationChoice, hasBranchChoice = fa
|
|
|
42920
43081
|
function renderSelectableRow({ label, isCursor }) {
|
|
42921
43082
|
const line = `${isCursor ? "> " : " "}${label}`;
|
|
42922
43083
|
const styled = fg11(PALETTE.bodyText)(line);
|
|
42923
|
-
return [isCursor ?
|
|
43084
|
+
return [isCursor ? bg4(PALETTE.selectionBg)(styled) : styled];
|
|
42924
43085
|
}
|
|
42925
43086
|
function renderRepositoryRow({ repository, isCursor, isChecked, source }) {
|
|
42926
43087
|
const marker = isChecked ? "[x]" : "[ ]";
|
|
@@ -42944,7 +43105,7 @@ async function createDefaultRenderer2() {
|
|
|
42944
43105
|
}
|
|
42945
43106
|
|
|
42946
43107
|
// src/tui/session-view.ts
|
|
42947
|
-
import { CliRenderEvents as CliRenderEvents5, bg as
|
|
43108
|
+
import { CliRenderEvents as CliRenderEvents5, bg as bg5, bold as bold5, dim as dim7, fg as fg12, link as link2, StyledText as StyledText11, stringToStyledText as stringToStyledText5, TextRenderable as TextRenderable5 } from "@opentui/core";
|
|
42948
43109
|
|
|
42949
43110
|
// src/tui/session-mcps-view.ts
|
|
42950
43111
|
import { stringToStyledText as stringToStyledText4 } from "@opentui/core";
|
|
@@ -43323,7 +43484,7 @@ async function createSessionTui({
|
|
|
43323
43484
|
`), joinStyled(liveContent, `
|
|
43324
43485
|
|
|
43325
43486
|
`)], ""));
|
|
43326
|
-
composer.placeholder = lifecycleRequestState.kind === "pending" ? `${lifecycleOperationPresentParticiple(lifecycleRequestState.operation)} the session \u2014 messages are paused.` : renderer.height <= 10 ? "Message Remy\u2026 \xB7 Enter sends" : idleComposerPlaceholder;
|
|
43487
|
+
composer.placeholder = latestState.historyStatus === "loading" ? "Draft a message\u2026 \xB7 sending waits for history" : lifecycleRequestState.kind === "pending" ? `${lifecycleOperationPresentParticiple(lifecycleRequestState.operation)} the session \u2014 messages are paused.` : renderer.height <= 10 ? "Message Remy\u2026 \xB7 Enter sends" : idleComposerPlaceholder;
|
|
43327
43488
|
resizeComposer(composer);
|
|
43328
43489
|
if (latestState.aggregateStatus === "open")
|
|
43329
43490
|
composerLayout.render();
|
|
@@ -43332,6 +43493,7 @@ async function createSessionTui({
|
|
|
43332
43493
|
const commandMenu = renderSlashCommandMenu({ commands: slashCompletions, selectedIndex: slashCompletionIndex, width: renderer.width - 2, maxRows: completionMenuRows(renderer.height) });
|
|
43333
43494
|
const agentSettingsMenu = agentSettingsPicker && latestState.aggregateStatus === "open" ? renderAgentSettingsPicker({ picker: agentSettingsPicker, width: renderer.width - 2, maxRows: completionMenuRows(renderer.height) }) : undefined;
|
|
43334
43495
|
const composerStatus = [
|
|
43496
|
+
latestState.historyStatus === "loading" ? renderFeedback({ kind: "progress", message: "Loading conversation\u2026 You can draft now; sending is paused." }) : undefined,
|
|
43335
43497
|
composerDraft.workspaceContexts?.length ? renderFeedback({ kind: "info", message: `${composerDraft.workspaceContexts.length} workspace context(s) \xB7 /context to review/remove` }) : undefined,
|
|
43336
43498
|
attachmentFeedback ? renderAttachmentFeedback(attachmentFeedback) : undefined,
|
|
43337
43499
|
composerFeedback ? renderFeedback(composerFeedback) : undefined,
|
|
@@ -43995,6 +44157,12 @@ ${segment.excerpt}`).join(`
|
|
|
43995
44157
|
render();
|
|
43996
44158
|
}
|
|
43997
44159
|
}
|
|
44160
|
+
transcriptScrollBox.onMouseDown = (event) => {
|
|
44161
|
+
if (latestState.aggregateStatus === "open" && event.button === 0) {
|
|
44162
|
+
event.preventDefault();
|
|
44163
|
+
composer.focus();
|
|
44164
|
+
}
|
|
44165
|
+
};
|
|
43998
44166
|
for (const text of [transcript, liveTranscript]) {
|
|
43999
44167
|
let pressedLink;
|
|
44000
44168
|
text.onMouseDown = (event) => {
|
|
@@ -44048,6 +44216,10 @@ ${segment.excerpt}`).join(`
|
|
|
44048
44216
|
}
|
|
44049
44217
|
if (!ownsAttachmentPreparation(preparationOwner))
|
|
44050
44218
|
return;
|
|
44219
|
+
if (latestState.historyStatus === "loading") {
|
|
44220
|
+
invalidateAttachmentPreparation();
|
|
44221
|
+
return;
|
|
44222
|
+
}
|
|
44051
44223
|
if (lifecycleRequestState.kind === "pending") {
|
|
44052
44224
|
composerFeedback = { kind: "progress", message: `${lifecycleOperationPresentParticiple(lifecycleRequestState.operation)} the session \u2014 messages are paused.` };
|
|
44053
44225
|
render();
|
|
@@ -44514,8 +44686,8 @@ function renderTerminalSessionBand({ aggregateStatus, width }) {
|
|
|
44514
44686
|
const status = sessionStatusLabel(aggregateStatus).toLowerCase();
|
|
44515
44687
|
return joinStyled([
|
|
44516
44688
|
new StyledText11([
|
|
44517
|
-
|
|
44518
|
-
|
|
44689
|
+
bg5(PALETTE.selectionBg)(bold5(fg12(PALETTE.systemAccent)(` Session ${status} `))),
|
|
44690
|
+
bg5(PALETTE.selectionBg)(fg12(PALETTE.bodyText)("\u2014 this conversation is closed. "))
|
|
44519
44691
|
]),
|
|
44520
44692
|
renderKeyHints({ hints: [{ key: "esc", action: "return to dashboard" }, { key: "n", action: "new session" }, { key: "m", action: "MCPs" }, { key: "ctrl+f", action: "files" }, { key: "ctrl+l", action: "links" }], width })
|
|
44521
44693
|
], `
|
|
@@ -45224,7 +45396,7 @@ function isPng(bytes) {
|
|
|
45224
45396
|
}
|
|
45225
45397
|
|
|
45226
45398
|
// src/tui/remy-splash.ts
|
|
45227
|
-
import { bold as bold6, BoxRenderable as BoxRenderable5, fg as fg13, NativeImage, StyledText as StyledText12, TextRenderable as TextRenderable6, bg as
|
|
45399
|
+
import { bold as bold6, BoxRenderable as BoxRenderable5, fg as fg13, NativeImage, StyledText as StyledText12, TextRenderable as TextRenderable6, bg as bg6 } from "@opentui/core";
|
|
45228
45400
|
|
|
45229
45401
|
// src/tui/remy-mark.ts
|
|
45230
45402
|
var remyPixelFieldSource = new URL("../assets/remy-pixel-field.png", import.meta.url);
|
|
@@ -45498,7 +45670,7 @@ function renderMark(field, frame, rows = markRows) {
|
|
|
45498
45670
|
const flush = () => {
|
|
45499
45671
|
if (runLength === 0)
|
|
45500
45672
|
return;
|
|
45501
|
-
chunks.push(fg13(grey(runTop))(
|
|
45673
|
+
chunks.push(fg13(grey(runTop))(bg6(grey(runBottom))(upperHalfBlock.repeat(runLength))));
|
|
45502
45674
|
runLength = 0;
|
|
45503
45675
|
};
|
|
45504
45676
|
for (let column = 0;column < columns; column++) {
|
|
@@ -46070,7 +46242,7 @@ async function dashboard({
|
|
|
46070
46242
|
const openDashboardTui = dependencies.openDashboardTui ?? createDashboardTui;
|
|
46071
46243
|
const pages = [initialSessions];
|
|
46072
46244
|
let pageIndex = 0;
|
|
46073
|
-
let filters = { statuses: [], creators: [] };
|
|
46245
|
+
let filters = { statuses: [], creators: [], repositories: [], branchName: "" };
|
|
46074
46246
|
let authors;
|
|
46075
46247
|
let activeDashboardGeneration = 0;
|
|
46076
46248
|
function holdTransitionScreen() {
|
|
@@ -46079,8 +46251,9 @@ async function dashboard({
|
|
|
46079
46251
|
function createDashboardPageLoader({ generation }) {
|
|
46080
46252
|
const loadingPages = new Map;
|
|
46081
46253
|
let pageLoadTail = Promise.resolve();
|
|
46254
|
+
let filterRequestNumber = 0;
|
|
46082
46255
|
return async (input) => {
|
|
46083
|
-
const requestKey = "filters" in input ? `filters:${
|
|
46256
|
+
const requestKey = "filters" in input ? `filters:${++filterRequestNumber}` : input.target;
|
|
46084
46257
|
const matchingPageLoad = loadingPages.get(requestKey);
|
|
46085
46258
|
if (matchingPageLoad)
|
|
46086
46259
|
return await matchingPageLoad;
|
|
@@ -46095,6 +46268,9 @@ async function dashboard({
|
|
|
46095
46268
|
limit: 20,
|
|
46096
46269
|
...nextFilters.statuses.length > 0 ? { statuses: nextFilters.statuses } : {},
|
|
46097
46270
|
...nextFilters.creators.length > 0 ? { creatorUserIds: nextFilters.creators.map((creator) => creator.id) } : {},
|
|
46271
|
+
...nextFilters.repositories.length > 0 ? { repositoryIds: nextFilters.repositories.map((repository) => repository.id) } : {},
|
|
46272
|
+
...nextFilters.branchName ? { branchName: nextFilters.branchName } : {},
|
|
46273
|
+
...nextFilters.search ? { search: nextFilters.search } : {},
|
|
46098
46274
|
signal: input.signal
|
|
46099
46275
|
}));
|
|
46100
46276
|
if (generation !== activeDashboardGeneration)
|
|
@@ -46113,6 +46289,9 @@ async function dashboard({
|
|
|
46113
46289
|
after,
|
|
46114
46290
|
...filters.statuses.length > 0 ? { statuses: filters.statuses } : {},
|
|
46115
46291
|
...filters.creators.length > 0 ? { creatorUserIds: filters.creators.map((creator) => creator.id) } : {},
|
|
46292
|
+
...filters.repositories.length > 0 ? { repositoryIds: filters.repositories.map((repository) => repository.id) } : {},
|
|
46293
|
+
...filters.branchName ? { branchName: filters.branchName } : {},
|
|
46294
|
+
...filters.search ? { search: filters.search } : {},
|
|
46116
46295
|
signal
|
|
46117
46296
|
}));
|
|
46118
46297
|
if (generation !== activeDashboardGeneration)
|
|
@@ -46133,6 +46312,9 @@ async function dashboard({
|
|
|
46133
46312
|
after: current.nextCursor,
|
|
46134
46313
|
...filters.statuses.length > 0 ? { statuses: filters.statuses } : {},
|
|
46135
46314
|
...filters.creators.length > 0 ? { creatorUserIds: filters.creators.map((creator) => creator.id) } : {},
|
|
46315
|
+
...filters.repositories.length > 0 ? { repositoryIds: filters.repositories.map((repository) => repository.id) } : {},
|
|
46316
|
+
...filters.branchName ? { branchName: filters.branchName } : {},
|
|
46317
|
+
...filters.search ? { search: filters.search } : {},
|
|
46136
46318
|
signal
|
|
46137
46319
|
}));
|
|
46138
46320
|
if (generation !== activeDashboardGeneration)
|
|
@@ -46172,6 +46354,9 @@ async function dashboard({
|
|
|
46172
46354
|
limit: 20,
|
|
46173
46355
|
...filters.statuses.length > 0 ? { statuses: filters.statuses } : {},
|
|
46174
46356
|
...filters.creators.length > 0 ? { creatorUserIds: filters.creators.map((creator) => creator.id) } : {},
|
|
46357
|
+
...filters.repositories.length > 0 ? { repositoryIds: filters.repositories.map((repository) => repository.id) } : {},
|
|
46358
|
+
...filters.branchName ? { branchName: filters.branchName } : {},
|
|
46359
|
+
...filters.search ? { search: filters.search } : {},
|
|
46175
46360
|
signal: dependencies.abortSignal
|
|
46176
46361
|
})));
|
|
46177
46362
|
pageIndex = 0;
|
|
@@ -46182,6 +46367,7 @@ async function dashboard({
|
|
|
46182
46367
|
const action = await renderRemyView({
|
|
46183
46368
|
open: async () => await openDashboardTui({
|
|
46184
46369
|
initialPage: pages[0],
|
|
46370
|
+
loadRepositories: async () => await repositories,
|
|
46185
46371
|
initialFilters: filters,
|
|
46186
46372
|
loadAuthors: loadDashboardAuthors,
|
|
46187
46373
|
loadPage: loadDashboardPage
|
|
@@ -46660,6 +46846,10 @@ var workspaceInvalidationEventSchema = exports_external2.object({
|
|
|
46660
46846
|
type: exports_external2.enum(["session.workspace.git.initialized", "session.workspace.git.committed", "session.workspace.git.pushed"]),
|
|
46661
46847
|
payload: exports_external2.object({ repository: exports_external2.object({ id: exports_external2.string().min(1) }) })
|
|
46662
46848
|
});
|
|
46849
|
+
var mainAgentTurnEndedEventSchema = exports_external2.object({
|
|
46850
|
+
type: exports_external2.literal("agent.turn.ended"),
|
|
46851
|
+
actor: exports_external2.object({ type: exports_external2.literal("main") })
|
|
46852
|
+
});
|
|
46663
46853
|
async function runAttachedSession({
|
|
46664
46854
|
dependencies,
|
|
46665
46855
|
operations,
|
|
@@ -46706,6 +46896,9 @@ async function runAttachedSession({
|
|
|
46706
46896
|
const aborted3 = interactive ? new Promise((_resolve, reject) => {
|
|
46707
46897
|
rejectAborted = reject;
|
|
46708
46898
|
}) : undefined;
|
|
46899
|
+
aborted3?.catch(() => {
|
|
46900
|
+
return;
|
|
46901
|
+
});
|
|
46709
46902
|
const abortHandler = () => {
|
|
46710
46903
|
stop();
|
|
46711
46904
|
rejectAborted(dependencies.abortSignal?.reason instanceof Error ? dependencies.abortSignal.reason : new Error("interrupted"));
|
|
@@ -46722,11 +46915,12 @@ async function runAttachedSession({
|
|
|
46722
46915
|
});
|
|
46723
46916
|
}
|
|
46724
46917
|
const startPromise = controller.start(start);
|
|
46725
|
-
await Promise
|
|
46726
|
-
|
|
46727
|
-
|
|
46728
|
-
|
|
46729
|
-
|
|
46918
|
+
const controllerFailed = startPromise.then(async () => await new Promise(() => {}));
|
|
46919
|
+
controllerFailed.catch(() => {
|
|
46920
|
+
return;
|
|
46921
|
+
});
|
|
46922
|
+
if (!interactive)
|
|
46923
|
+
await Promise.race([controller.waitUntilReady(), startPromise, ...aborted3 ? [aborted3] : []]);
|
|
46730
46924
|
if (interactive) {
|
|
46731
46925
|
if (dependencies.abortSignal?.aborted)
|
|
46732
46926
|
throw dependencies.abortSignal.reason ?? new Error("interrupted");
|
|
@@ -46799,7 +46993,7 @@ async function runAttachedSession({
|
|
|
46799
46993
|
savePastedImage: async (input) => await writePastedImage({ ...input, temporaryDirectory: tmpdir2() }),
|
|
46800
46994
|
attachPromptPaths: async (input) => await uploadPromptPaths({ operations, ...input })
|
|
46801
46995
|
}),
|
|
46802
|
-
waitForAction: async (tui) => await Promise.race([tui.waitForAction(), aborted3])
|
|
46996
|
+
waitForAction: async (tui) => await Promise.race([tui.waitForAction(), aborted3, controllerFailed])
|
|
46803
46997
|
});
|
|
46804
46998
|
if (!action)
|
|
46805
46999
|
throw dependencies.abortSignal?.reason ?? new Error("interrupted");
|
|
@@ -46852,6 +47046,10 @@ async function runAttachedSession({
|
|
|
46852
47046
|
connection = state.connectionStatus;
|
|
46853
47047
|
if (frame?.kind !== "retained")
|
|
46854
47048
|
return;
|
|
47049
|
+
if (mainAgentTurnEndedEventSchema.safeParse(frame.data.event).success) {
|
|
47050
|
+
invalidateWorkspace({ kind: "turn-ended" });
|
|
47051
|
+
return;
|
|
47052
|
+
}
|
|
46855
47053
|
const event = workspaceInvalidationEventSchema.safeParse(frame.data.event);
|
|
46856
47054
|
if (!event.success)
|
|
46857
47055
|
return;
|
|
@@ -46882,7 +47080,7 @@ async function runAttachedSession({
|
|
|
46882
47080
|
view.invalidate(event);
|
|
46883
47081
|
}
|
|
46884
47082
|
mountedView = view;
|
|
46885
|
-
return await Promise.race([view.waitForAction(), aborted3]);
|
|
47083
|
+
return await Promise.race([view.waitForAction(), aborted3, controllerFailed]);
|
|
46886
47084
|
}
|
|
46887
47085
|
});
|
|
46888
47086
|
if (!result2 || result2.kind === "interrupt")
|