hop2 0.11.53 → 0.11.54
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.
|
@@ -28,7 +28,8 @@ import {
|
|
|
28
28
|
type SessionOriginScope,
|
|
29
29
|
type SwitcherFolder,
|
|
30
30
|
type SwitcherSession,
|
|
31
|
-
type SwitcherSortMode
|
|
31
|
+
type SwitcherSortMode,
|
|
32
|
+
insertAfter
|
|
32
33
|
} from "../utils/switcherModel";
|
|
33
34
|
import { scanKeyboardProtocol } from "../utils/keyboardProtocol";
|
|
34
35
|
import { DigestCard } from "./DigestCard";
|
|
@@ -2452,11 +2453,16 @@ export const SessionSwitcher = ({
|
|
|
2452
2453
|
headers: { "Content-Type": "application/json" },
|
|
2453
2454
|
body: JSON.stringify({ internalName: sessionKey(s), ...(target ? { target } : {}) })
|
|
2454
2455
|
});
|
|
2455
|
-
const data = await res.json().catch(() => ({} as { error?: string; name?: string; kind?: string; extract?: { turns?: number; bytes?: number } }));
|
|
2456
|
+
const data = await res.json().catch(() => ({} as { error?: string; name?: string; internalName?: string; kind?: string; extract?: { turns?: number; bytes?: number } }));
|
|
2456
2457
|
if (!res.ok) {
|
|
2457
2458
|
onNotice(data.error || (target ? "Handoff failed" : "Fork failed"));
|
|
2458
2459
|
return;
|
|
2459
2460
|
}
|
|
2461
|
+
// The new session belongs beside its source: the daemon files it in
|
|
2462
|
+
// the same folder; on a hand-arranged wall it takes the next slot.
|
|
2463
|
+
if (typeof data.internalName === "string" && data.internalName) {
|
|
2464
|
+
persistManualOrder(insertAfter(manualOrder, sessionKey(s), data.internalName));
|
|
2465
|
+
}
|
|
2460
2466
|
if (data.kind === "handoff") {
|
|
2461
2467
|
const kb = Math.max(1, Math.round((data.extract?.bytes || 0) / 1024));
|
|
2462
2468
|
onNotice(`${data.name} — ${data.extract?.turns ?? "?"} turns handed off (${kb} KB); it reads them first`);
|
|
@@ -266,6 +266,19 @@ const orderManually = (sessions: SwitcherSession[], manualOrder: string[]): Swit
|
|
|
266
266
|
});
|
|
267
267
|
};
|
|
268
268
|
|
|
269
|
+
/**
|
|
270
|
+
* Place `key` right after `afterKey` in a manual order. A source the user
|
|
271
|
+
* never placed leaves the order alone: both then sort by name at the tail,
|
|
272
|
+
* where a fork's name already follows its source's.
|
|
273
|
+
*/
|
|
274
|
+
export const insertAfter = (order: string[], afterKey: string, key: string): string[] => {
|
|
275
|
+
const at = order.indexOf(afterKey);
|
|
276
|
+
if (at < 0 || afterKey === key) return order;
|
|
277
|
+
const without = order.filter((k) => k !== key);
|
|
278
|
+
const i = without.indexOf(afterKey);
|
|
279
|
+
return [...without.slice(0, i + 1), key, ...without.slice(i + 1)];
|
|
280
|
+
};
|
|
281
|
+
|
|
269
282
|
/**
|
|
270
283
|
* Split sessions into the user's folders plus everything still loose.
|
|
271
284
|
* Folders belong to MANUAL mode only: Recent is ordered by the machine and
|
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
filterSessionsByOrigin,
|
|
6
6
|
projectKey,
|
|
7
7
|
relativeTime,
|
|
8
|
-
type SwitcherSession
|
|
8
|
+
type SwitcherSession,
|
|
9
|
+
insertAfter
|
|
9
10
|
} from "../src/utils/switcherModel";
|
|
10
11
|
|
|
11
12
|
const mk = (over: Partial<SwitcherSession> & { name: string }): SwitcherSession => ({
|
|
@@ -523,3 +524,15 @@ describe("origin scope vs foldered sessions", () => {
|
|
|
523
524
|
expect(filterSessionsByOrigin(sessions, "agent").map((s) => s.name)).toContain("mybot");
|
|
524
525
|
});
|
|
525
526
|
});
|
|
527
|
+
|
|
528
|
+
describe("insertAfter — a fork takes the slot after its source", () => {
|
|
529
|
+
it("inserts right after a placed source, moving an existing entry", () => {
|
|
530
|
+
expect(insertAfter(["a", "b", "c"], "a", "x")).toEqual(["a", "x", "b", "c"]);
|
|
531
|
+
expect(insertAfter(["a", "b", "x"], "a", "x")).toEqual(["a", "x", "b"]);
|
|
532
|
+
});
|
|
533
|
+
it("leaves the order alone when the source was never placed, or is itself", () => {
|
|
534
|
+
const order = ["a", "b"];
|
|
535
|
+
expect(insertAfter(order, "zzz", "x")).toBe(order);
|
|
536
|
+
expect(insertAfter(order, "a", "a")).toBe(order);
|
|
537
|
+
});
|
|
538
|
+
});
|
package/hop
CHANGED
|
@@ -11769,18 +11769,24 @@ happens until someone clicks it.
|
|
|
11769
11769
|
saveDefinition: false,
|
|
11770
11770
|
command
|
|
11771
11771
|
});
|
|
11772
|
+
// Filed where its source is. The effective config is the
|
|
11773
|
+
// wall's own truth for the folder (saved, runtime and
|
|
11774
|
+
// durable layers merged); the durable meta alone missed
|
|
11775
|
+
// sessions filed before folders were written there.
|
|
11776
|
+
const folderId = config.folderId || meta?.folderId || null;
|
|
11772
11777
|
rememberRuntimeSessionMetadata(forkInternal, {
|
|
11773
11778
|
type: 'terminal',
|
|
11774
11779
|
displayName: forkName,
|
|
11775
11780
|
cwd,
|
|
11776
11781
|
createdBy,
|
|
11777
|
-
folderId:
|
|
11782
|
+
folderId: folderId || undefined
|
|
11778
11783
|
});
|
|
11779
11784
|
writeClaudeSessionMeta(forkInternal, {
|
|
11780
11785
|
internalName: forkInternal,
|
|
11781
11786
|
displayName: forkName,
|
|
11782
11787
|
cwd,
|
|
11783
11788
|
createdBy,
|
|
11789
|
+
folderId: folderId || undefined,
|
|
11784
11790
|
forkedFrom: source,
|
|
11785
11791
|
...(handoff ? { handoffFrom: sourceKind, handoffTo: wantTarget } : {})
|
|
11786
11792
|
});
|
package/package.json
CHANGED