@cueloop/client 0.1.0-alpha.69 → 0.1.0-alpha.70
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.appearance.test.tsx +2 -2
- package/src/App.autoclose.test.tsx +2 -2
- package/src/App.diff.test.tsx +2 -2
- package/src/App.export.test.tsx +2 -2
- package/src/App.plan-review.test.tsx +2 -2
- package/src/App.prototype-markdown.test.tsx +2 -2
- package/src/App.prototype.test.tsx +2 -2
- package/src/App.readonly.test.tsx +2 -2
- package/src/App.rendering.test.tsx +2 -2
- package/src/App.submit-confirm.test.tsx +2 -2
- package/src/App.test.tsx +2 -2
- package/src/App.toast.test.tsx +3 -3
- package/src/App.tsx +240 -228
- package/src/App.walk.test.tsx +2 -2
- package/src/App.workbench.test.tsx +2 -2
- package/src/app-key-state.ts +3 -3
- package/src/app-screens.tsx +4 -4
- package/src/app-view-model.ts +6 -6
- package/src/components/AnnotatableFileView.stories.tsx +2 -2
- package/src/components/AnnotatableFileView.test.tsx +2 -2
- package/src/components/AnnotatableFileView.tsx +2 -2
- package/src/components/BareWorkbenchFileView.tsx +2 -7
- package/src/components/ChangesColumn.tsx +2 -2
- package/src/components/DiffContentView.tsx +2 -2
- package/src/components/ThreadView.selection.test.tsx +5 -5
- package/src/components/ThreadView.slash.test.tsx +49 -4
- package/src/components/ThreadView.tsx +2 -2
- package/src/components/WelcomePlayground.tsx +2 -2
- package/src/components/__snapshots__/stories.test.tsx.snap +6 -6
- package/src/components/agent-launcher.tsx +2 -2
- package/src/components/quick-actions-editor.test.tsx +49 -0
- package/src/components/quick-actions-editor.tsx +32 -20
- package/src/components/session-tree.test.ts +2 -2
- package/src/components/session-tree.ts +10 -10
- package/src/components/story-fixtures.ts +3 -3
- package/src/config.test.ts +30 -1
- package/src/config.ts +19 -0
- package/src/discussions.ts +2 -2
- package/src/intent-dispatch.test.ts +3 -6
- package/src/intent-dispatch.ts +6 -6
- package/src/serve.test.ts +2 -2
- package/src/share.ts +9 -14
- package/src/skills.test.ts +74 -0
- package/src/skills.ts +68 -0
- package/src/slash-palette.ts +10 -3
- package/src/{session-controller.curate.test.ts → thread-controller.curate.test.ts} +8 -7
- package/src/{session-controller.live-diff.test.ts → thread-controller.live-diff.test.ts} +7 -6
- package/src/{session-controller.share.test.ts → thread-controller.share.test.ts} +14 -13
- package/src/{session-controller.tree.test.ts → thread-controller.tree.test.ts} +8 -7
- package/src/{session-controller.ts → thread-controller.ts} +18 -22
- package/src/use-annotation-surface.tsx +14 -10
- package/src/view-diff.ts +2 -2
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, mock, test, type Mock } from "bun:test";
|
|
2
2
|
import { ManualClock } from "@opentui/core/testing";
|
|
3
|
-
import { SCHEMA_VERSION, type Annotation, type
|
|
3
|
+
import { SCHEMA_VERSION, type Annotation, type Thread } from "@cueloop/schema";
|
|
4
4
|
import type { SessionClient } from "@cueloop/daemon/client";
|
|
5
5
|
import {
|
|
6
6
|
createReviewController,
|
|
7
7
|
SHARE_RECONNECT_MAX_MS,
|
|
8
8
|
SHARE_RECONNECT_MIN_MS,
|
|
9
9
|
type ShareTransport,
|
|
10
|
-
} from "./
|
|
10
|
+
} from "./thread-controller";
|
|
11
11
|
import { mergeFromShare, type ShareWatchHandlers } from "./share";
|
|
12
12
|
|
|
13
13
|
const publishShare = mock(async () => ({ line: "ssh p_abc123xy@cueloop.dev", copied: true }));
|
|
14
|
-
let remote:
|
|
14
|
+
let remote: Thread;
|
|
15
15
|
const pullShare = mock(async () => remote);
|
|
16
16
|
const pushShare = mock(
|
|
17
17
|
async (_shareId: string, _annotations: Array<Omit<Annotation, "createdAt">>) => {},
|
|
@@ -27,7 +27,7 @@ const shareTransport: ShareTransport = {
|
|
|
27
27
|
mergeFromShare,
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
function sessionFixture(overrides: Partial<
|
|
30
|
+
function sessionFixture(overrides: Partial<Thread> = {}): Thread {
|
|
31
31
|
return {
|
|
32
32
|
schemaVersion: SCHEMA_VERSION,
|
|
33
33
|
id: "ses_1",
|
|
@@ -54,19 +54,20 @@ function annotation(id: string, author: string): Annotation {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
interface FakeSessionClient extends SessionClient {
|
|
57
|
-
|
|
57
|
+
sessionComment: Mock<SessionClient["sessionComment"]>;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
const unimplemented = (member: string) => () =>
|
|
61
61
|
Promise.reject(new Error(`fakeClient does not implement ${member}`));
|
|
62
62
|
|
|
63
|
-
function fakeClient(session:
|
|
63
|
+
function fakeClient(session: Thread): FakeSessionClient {
|
|
64
64
|
return {
|
|
65
65
|
onEvent: () => () => {},
|
|
66
66
|
subscribe: async () => {},
|
|
67
67
|
sessionGet: async () => session,
|
|
68
68
|
sessionList: async () => [session],
|
|
69
|
-
|
|
69
|
+
sessionComment: mock<SessionClient["sessionComment"]>(async () => session),
|
|
70
|
+
sessionAnnotate: unimplemented("sessionAnnotate"),
|
|
70
71
|
sessionRemoveAnnotation: unimplemented("sessionRemoveAnnotation"),
|
|
71
72
|
sessionSetWorkingCopy: unimplemented("sessionSetWorkingCopy"),
|
|
72
73
|
sessionCutBlock: unimplemented("sessionCutBlock"),
|
|
@@ -100,7 +101,7 @@ function fakeClient(session: ReviewSession): FakeSessionClient {
|
|
|
100
101
|
const tick = () => new Promise((resolve) => setTimeout(resolve, 0));
|
|
101
102
|
|
|
102
103
|
async function connectedController(
|
|
103
|
-
session:
|
|
104
|
+
session: Thread,
|
|
104
105
|
clock?: ManualClock,
|
|
105
106
|
transport: ShareTransport = shareTransport,
|
|
106
107
|
): Promise<{ controller: ReturnType<typeof createReviewController>; client: FakeSessionClient }> {
|
|
@@ -233,7 +234,7 @@ describe("mirror on annotate", () => {
|
|
|
233
234
|
sessionFixture({ shareId: "p_abc123xy", annotations: [annotation("a1", "SHA256:me")] }),
|
|
234
235
|
);
|
|
235
236
|
|
|
236
|
-
client.
|
|
237
|
+
client.sessionComment.mockImplementationOnce(async () => {
|
|
237
238
|
throw new Error("session is resolved");
|
|
238
239
|
});
|
|
239
240
|
|
|
@@ -259,7 +260,7 @@ describe("reply", () => {
|
|
|
259
260
|
await tick();
|
|
260
261
|
|
|
261
262
|
// Assert
|
|
262
|
-
const wire = client.
|
|
263
|
+
const wire = client.sessionComment.mock.calls.at(-1)![1];
|
|
263
264
|
|
|
264
265
|
expect(wire).toEqual({
|
|
265
266
|
id: id!,
|
|
@@ -287,7 +288,7 @@ describe("reply", () => {
|
|
|
287
288
|
await tick();
|
|
288
289
|
|
|
289
290
|
// Assert
|
|
290
|
-
expect(client.
|
|
291
|
+
expect(client.sessionComment.mock.calls.at(-1)?.[1]).toMatchObject({
|
|
291
292
|
id: "a2",
|
|
292
293
|
body: "revised reply",
|
|
293
294
|
replyTo: "a1",
|
|
@@ -310,7 +311,7 @@ describe("reply", () => {
|
|
|
310
311
|
await tick();
|
|
311
312
|
|
|
312
313
|
// Assert
|
|
313
|
-
expect(client.
|
|
314
|
+
expect(client.sessionComment.mock.calls.at(-1)?.[1]).toMatchObject({ replyTo: "a1" });
|
|
314
315
|
});
|
|
315
316
|
|
|
316
317
|
test("replying to an unknown annotation does nothing", async () => {
|
|
@@ -319,7 +320,7 @@ describe("reply", () => {
|
|
|
319
320
|
|
|
320
321
|
// Act + Assert
|
|
321
322
|
expect(controller.reply("missing", "x")).toBeUndefined();
|
|
322
|
-
expect(client.
|
|
323
|
+
expect(client.sessionComment).not.toHaveBeenCalled();
|
|
323
324
|
});
|
|
324
325
|
});
|
|
325
326
|
|
|
@@ -12,10 +12,10 @@ import {
|
|
|
12
12
|
SCHEMA_VERSION,
|
|
13
13
|
switchBranch,
|
|
14
14
|
type Annotation,
|
|
15
|
-
type
|
|
15
|
+
type Thread,
|
|
16
16
|
} from "@cueloop/schema";
|
|
17
17
|
import type { SessionClient } from "@cueloop/daemon/client";
|
|
18
|
-
import { createReviewController, type ShareTransport } from "./
|
|
18
|
+
import { createReviewController, type ShareTransport } from "./thread-controller";
|
|
19
19
|
import { mergeFromShare } from "./share";
|
|
20
20
|
|
|
21
21
|
const AT = "2026-01-01T00:00:00.000Z";
|
|
@@ -31,8 +31,8 @@ function annotation(id: string, body: string): Annotation {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/** A plan with one comment on main and its history. */
|
|
34
|
-
function sessionFixture():
|
|
35
|
-
const session:
|
|
34
|
+
function sessionFixture(): Thread {
|
|
35
|
+
const session: Thread = {
|
|
36
36
|
schemaVersion: SCHEMA_VERSION,
|
|
37
37
|
id: "ses_1",
|
|
38
38
|
workspace: { repoRoot: "/repo", branch: "main" },
|
|
@@ -49,7 +49,7 @@ function sessionFixture(): ReviewSession {
|
|
|
49
49
|
return session;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const publish = mock(async (session:
|
|
52
|
+
const publish = mock(async (session: Thread) => ({
|
|
53
53
|
line: `ssh p_${session.id}@cueloop.dev`,
|
|
54
54
|
copied: true,
|
|
55
55
|
}));
|
|
@@ -67,14 +67,15 @@ const unimplemented = (member: string) => () =>
|
|
|
67
67
|
Promise.reject(new Error(`fakeClient does not implement ${member}`));
|
|
68
68
|
|
|
69
69
|
/** Tree requests never resolve here: what the controller shows is its own guess. */
|
|
70
|
-
const pending = () => new Promise<
|
|
70
|
+
const pending = () => new Promise<Thread>(() => {});
|
|
71
71
|
|
|
72
|
-
function fakeClient(session:
|
|
72
|
+
function fakeClient(session: Thread) {
|
|
73
73
|
return {
|
|
74
74
|
onEvent: () => () => {},
|
|
75
75
|
subscribe: async () => {},
|
|
76
76
|
sessionGet: async () => session,
|
|
77
77
|
sessionList: async () => [session],
|
|
78
|
+
sessionComment: unimplemented("sessionComment"),
|
|
78
79
|
sessionAnnotate: unimplemented("sessionAnnotate"),
|
|
79
80
|
sessionRemoveAnnotation: unimplemented("sessionRemoveAnnotation"),
|
|
80
81
|
sessionSetWorkingCopy: unimplemented("sessionSetWorkingCopy"),
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
type Annotation,
|
|
31
31
|
type AnnotationTarget,
|
|
32
32
|
type DiffFileContents,
|
|
33
|
-
type
|
|
33
|
+
type Thread,
|
|
34
34
|
type SessionHistory,
|
|
35
35
|
type VerdictKind,
|
|
36
36
|
} from "@cueloop/schema";
|
|
@@ -124,8 +124,8 @@ function planCutId(base: { lineStart: number; lineEnd: number }): string {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
export interface ControllerSnapshot {
|
|
127
|
-
session:
|
|
128
|
-
inbox:
|
|
127
|
+
session: Thread | null;
|
|
128
|
+
inbox: Thread[] | null;
|
|
129
129
|
status: string;
|
|
130
130
|
toast: ToastState | null;
|
|
131
131
|
error: string | null;
|
|
@@ -360,7 +360,7 @@ class Controller implements ReviewController {
|
|
|
360
360
|
private shareStop: (() => void) | null = null;
|
|
361
361
|
private shareRun: object | null = null;
|
|
362
362
|
/** Projections keyed by session identity (and the live diff for non-diff threads) so renders reuse one computation. */
|
|
363
|
-
private derivedFor:
|
|
363
|
+
private derivedFor: Thread | null = null;
|
|
364
364
|
/** The launch/workspace repo's live working-tree diff, feeding the Changes view for non-diff threads. */
|
|
365
365
|
private liveDiff: LiveWorkingDiff | null = null;
|
|
366
366
|
private derivedForLiveDiff: LiveWorkingDiff | null = null;
|
|
@@ -661,7 +661,7 @@ class Controller implements ReviewController {
|
|
|
661
661
|
}
|
|
662
662
|
|
|
663
663
|
/** Optimistic apply: the daemon response is the next session snapshot. */
|
|
664
|
-
private apply(mutation: Promise<
|
|
664
|
+
private apply(mutation: Promise<Thread>): void {
|
|
665
665
|
mutation
|
|
666
666
|
.then((session) => this.update({ session }))
|
|
667
667
|
.catch((cause: unknown) =>
|
|
@@ -674,7 +674,7 @@ class Controller implements ReviewController {
|
|
|
674
674
|
* builds on the first instead of on the stale snapshot. Answers arrive in
|
|
675
675
|
* request order over the socket and replace the guess with the daemon's copy.
|
|
676
676
|
*/
|
|
677
|
-
private applyOptimistic(expected:
|
|
677
|
+
private applyOptimistic(expected: Thread, mutation: Promise<Thread>): void {
|
|
678
678
|
this.update({ session: expected });
|
|
679
679
|
mutation
|
|
680
680
|
.then((session) => this.update({ session }))
|
|
@@ -762,7 +762,7 @@ class Controller implements ReviewController {
|
|
|
762
762
|
);
|
|
763
763
|
|
|
764
764
|
if (baseIndex === -1) return;
|
|
765
|
-
const expected:
|
|
765
|
+
const expected: Thread = {
|
|
766
766
|
...session,
|
|
767
767
|
workingCopy: restoreBlock(base, working, block.base, line),
|
|
768
768
|
};
|
|
@@ -847,7 +847,7 @@ class Controller implements ReviewController {
|
|
|
847
847
|
const session = this.snapshot.session;
|
|
848
848
|
|
|
849
849
|
if (!session) return;
|
|
850
|
-
const expected:
|
|
850
|
+
const expected: Thread = { ...session, curation: rejections };
|
|
851
851
|
|
|
852
852
|
if (!rejections.length) delete expected.curation;
|
|
853
853
|
this.applyOptimistic(expected, this.client!.sessionCurate(session.id, rejections));
|
|
@@ -980,7 +980,7 @@ class Controller implements ReviewController {
|
|
|
980
980
|
* resolving stay stored (the feedback serializer flags orphaned anchors);
|
|
981
981
|
* the count feeds the one-line banner above the sheet.
|
|
982
982
|
*/
|
|
983
|
-
private reconcileAnnotations(session:
|
|
983
|
+
private reconcileAnnotations(session: Thread, editedContent: string): void {
|
|
984
984
|
const editedBlocks = parseBlocks(editedContent);
|
|
985
985
|
const orphanCount = session.annotations.filter(
|
|
986
986
|
(annotation) => resolveAnchor(annotation.anchor, editedBlocks) === null,
|
|
@@ -1049,7 +1049,7 @@ class Controller implements ReviewController {
|
|
|
1049
1049
|
resolvedTarget.kind === "artifact"
|
|
1050
1050
|
? { id: newAnnotationId(), kind, anchor, body }
|
|
1051
1051
|
: { id: newAnnotationId(), kind, anchor, body, target: resolvedTarget };
|
|
1052
|
-
const persisted = this.client!.
|
|
1052
|
+
const persisted = this.client!.sessionComment(session.id, wire);
|
|
1053
1053
|
|
|
1054
1054
|
this.apply(persisted);
|
|
1055
1055
|
this.mirrorAnnotation(persisted, wire);
|
|
@@ -1065,7 +1065,7 @@ class Controller implements ReviewController {
|
|
|
1065
1065
|
target.kind === "artifact"
|
|
1066
1066
|
? { id: newAnnotationId(), kind: "comment", anchor, body }
|
|
1067
1067
|
: { id: newAnnotationId(), kind: "comment", anchor, body, target };
|
|
1068
|
-
const persisted = this.client!.
|
|
1068
|
+
const persisted = this.client!.sessionComment(session.id, wire);
|
|
1069
1069
|
|
|
1070
1070
|
this.apply(persisted);
|
|
1071
1071
|
this.mirrorAnnotation(persisted, wire);
|
|
@@ -1108,7 +1108,7 @@ class Controller implements ReviewController {
|
|
|
1108
1108
|
replyTo: root.replyTo ?? root.id,
|
|
1109
1109
|
};
|
|
1110
1110
|
const wire = root.target ? { ...base, target: root.target } : base;
|
|
1111
|
-
const persisted = this.client!.
|
|
1111
|
+
const persisted = this.client!.sessionComment(session.id, wire);
|
|
1112
1112
|
|
|
1113
1113
|
this.apply(persisted);
|
|
1114
1114
|
this.mirrorAnnotation(persisted, wire);
|
|
@@ -1122,7 +1122,7 @@ class Controller implements ReviewController {
|
|
|
1122
1122
|
if (!session) return undefined;
|
|
1123
1123
|
const anchor = { quote, prefix: "", suffix: "", selector };
|
|
1124
1124
|
const wire = { id: newAnnotationId(), kind: "comment", anchor, body };
|
|
1125
|
-
const persisted = this.client!.
|
|
1125
|
+
const persisted = this.client!.sessionComment(session.id, wire);
|
|
1126
1126
|
|
|
1127
1127
|
this.apply(persisted);
|
|
1128
1128
|
this.mirrorAnnotation(persisted, wire);
|
|
@@ -1147,7 +1147,7 @@ class Controller implements ReviewController {
|
|
|
1147
1147
|
};
|
|
1148
1148
|
|
|
1149
1149
|
if (existing.replyTo !== undefined) wire.replyTo = existing.replyTo;
|
|
1150
|
-
const persisted = this.client!.
|
|
1150
|
+
const persisted = this.client!.sessionComment(session.id, wire);
|
|
1151
1151
|
|
|
1152
1152
|
this.apply(persisted);
|
|
1153
1153
|
this.mirrorAnnotation(persisted, wire);
|
|
@@ -1156,7 +1156,7 @@ class Controller implements ReviewController {
|
|
|
1156
1156
|
|
|
1157
1157
|
// push only after the local write lands, so a rejected write never leaks to the share
|
|
1158
1158
|
private mirrorAnnotation(
|
|
1159
|
-
persisted: Promise<
|
|
1159
|
+
persisted: Promise<Thread>,
|
|
1160
1160
|
annotation: Omit<Annotation, "createdAt">,
|
|
1161
1161
|
): void {
|
|
1162
1162
|
const session = this.snapshot.session;
|
|
@@ -1391,12 +1391,8 @@ class Controller implements ReviewController {
|
|
|
1391
1391
|
* Show a moved tree at once - the record re-derived from its new path - and
|
|
1392
1392
|
* let the daemon's answer replace it. A refused move surfaces as status.
|
|
1393
1393
|
*/
|
|
1394
|
-
private moveTree(
|
|
1395
|
-
|
|
1396
|
-
history: SessionHistory,
|
|
1397
|
-
request: () => Promise<ReviewSession>,
|
|
1398
|
-
): void {
|
|
1399
|
-
const expected: ReviewSession = { ...session, history };
|
|
1394
|
+
private moveTree(session: Thread, history: SessionHistory, request: () => Promise<Thread>): void {
|
|
1395
|
+
const expected: Thread = { ...session, history };
|
|
1400
1396
|
|
|
1401
1397
|
applyPathView(
|
|
1402
1398
|
expected,
|
|
@@ -1421,7 +1417,7 @@ class Controller implements ReviewController {
|
|
|
1421
1417
|
.catch(() => {});
|
|
1422
1418
|
}
|
|
1423
1419
|
|
|
1424
|
-
private mergeShared(remote:
|
|
1420
|
+
private mergeShared(remote: Thread): Promise<void> {
|
|
1425
1421
|
const session = this.snapshot.session;
|
|
1426
1422
|
|
|
1427
1423
|
if (!session || !this.client) return Promise.resolve();
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
* cards this hook builds under the visual line a span ends on.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import React, { useEffect, useRef, useState } from "react";
|
|
15
|
+
import React, { useContext, useEffect, useRef, useState } from "react";
|
|
16
16
|
import type { KeyEvent, MouseEvent as TerminalMouseEvent, TextRenderable } from "@opentui/core";
|
|
17
17
|
import { useKeyboard } from "@opentui/react";
|
|
18
|
-
import type {
|
|
18
|
+
import type { Thread } from "@cueloop/schema";
|
|
19
19
|
import type { Mark } from "./view-plan";
|
|
20
20
|
import type { QuickAction } from "./config";
|
|
21
21
|
import type { Theme } from "./theme";
|
|
@@ -37,7 +37,13 @@ import {
|
|
|
37
37
|
} from "./thread-selection";
|
|
38
38
|
import { annotationPaletteFor, type AnnotationPalette } from "./annotation-palette";
|
|
39
39
|
import { printableSequence, type MarkRange, type VisualLine } from "./mark-runs";
|
|
40
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
mergeSlashItems,
|
|
42
|
+
resolveInlineSuggestion,
|
|
43
|
+
slashFilter,
|
|
44
|
+
slashItemsFrom,
|
|
45
|
+
} from "./slash-palette";
|
|
46
|
+
import { SlashSkillsContext } from "./skills";
|
|
41
47
|
import { discussionsFrom, type Discussion } from "./discussions";
|
|
42
48
|
import {
|
|
43
49
|
CommentRow,
|
|
@@ -68,7 +74,7 @@ export interface ComposeState {
|
|
|
68
74
|
|
|
69
75
|
export interface AnnotationSurfaceOptions {
|
|
70
76
|
source: LineSource;
|
|
71
|
-
session:
|
|
77
|
+
session: Thread;
|
|
72
78
|
marks: Map<number, Mark[]>;
|
|
73
79
|
quickActions: QuickAction[];
|
|
74
80
|
tokens: Theme;
|
|
@@ -305,14 +311,12 @@ export function useAnnotationSurface(options: AnnotationSurfaceOptions): Annotat
|
|
|
305
311
|
setCompose(null);
|
|
306
312
|
setComposeText("");
|
|
307
313
|
};
|
|
314
|
+
const skills = useContext(SlashSkillsContext);
|
|
315
|
+
const paletteItems = mergeSlashItems(slashItemsFrom(quickActions), skills);
|
|
308
316
|
// open only while typing the token: a leading "/" with no space yet
|
|
309
317
|
const slashActive = compose !== null && /^\/\S*$/.test(composeText);
|
|
310
|
-
const slashItems = slashActive
|
|
311
|
-
|
|
312
|
-
: [];
|
|
313
|
-
// inline skill completion: the trailing "/word" token and its closest match,
|
|
314
|
-
// offered as a hint below the composer that tab completes to the full name
|
|
315
|
-
const inlineSlash = resolveInlineSuggestion(slashActive, composeText, quickActions);
|
|
318
|
+
const slashItems = slashActive ? slashFilter(paletteItems, composeText.slice(1).trim()) : [];
|
|
319
|
+
const inlineSlash = resolveInlineSuggestion(slashActive, composeText, paletteItems);
|
|
316
320
|
|
|
317
321
|
const saveComment = (body: string): void => {
|
|
318
322
|
// the body saves verbatim - typed newlines are the author's choice;
|
package/src/view-diff.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
resolveAnchor,
|
|
13
13
|
type Annotation,
|
|
14
14
|
type Block,
|
|
15
|
-
type
|
|
15
|
+
type Thread,
|
|
16
16
|
} from "@cueloop/schema";
|
|
17
17
|
import { spanRangeInBlock, type TextSpan } from "./thread-selection";
|
|
18
18
|
import type { Mark } from "./view-plan";
|
|
@@ -253,7 +253,7 @@ export function fileTargetMarks(
|
|
|
253
253
|
* replies) toward the file its span ends in. Feeds the changed-files tree and tab badges, so a
|
|
254
254
|
* file whose tab is closed still shows it carries feedback.
|
|
255
255
|
*/
|
|
256
|
-
export function commentCountsByFile(session:
|
|
256
|
+
export function commentCountsByFile(session: Thread, rows: DiffRow[]): Map<string, number> {
|
|
257
257
|
const discussions = discussionsFrom(session, marksByRows(session.annotations, rows));
|
|
258
258
|
const counts = new Map<string, number>();
|
|
259
259
|
|