@cueloop/client 0.1.0-alpha.91 → 0.1.0-alpha.92
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cueloop/client",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.92",
|
|
4
4
|
"description": "The cueloop review UI: projection renderer, keyboard grammar, review rail",
|
|
5
5
|
"homepage": "https://github.com/mmurakaru/cueloop#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@cueloop/daemon": "0.1.0-alpha.
|
|
31
|
-
"@cueloop/extension-api": "0.1.0-alpha.
|
|
32
|
-
"@cueloop/integration-obsidian": "0.1.0-alpha.
|
|
33
|
-
"@cueloop/schema": "0.1.0-alpha.
|
|
30
|
+
"@cueloop/daemon": "0.1.0-alpha.92",
|
|
31
|
+
"@cueloop/extension-api": "0.1.0-alpha.92",
|
|
32
|
+
"@cueloop/integration-obsidian": "0.1.0-alpha.92",
|
|
33
|
+
"@cueloop/schema": "0.1.0-alpha.92",
|
|
34
34
|
"@opentui/core": "^0.5.11",
|
|
35
35
|
"@opentui/keymap": "0.5.11",
|
|
36
36
|
"@opentui/react": "^0.5.11",
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { describe, expect, mock, test } from "bun:test";
|
|
7
|
+
import type { RGBA } from "@opentui/core";
|
|
7
8
|
import { testRender } from "@opentui/react/test-utils";
|
|
8
9
|
import React from "react";
|
|
9
10
|
import { SCHEMA_VERSION, type Anchor, type Thread } from "@cueloop/schema";
|
|
10
11
|
import { AnnotatableFileView } from "./AnnotatableFileView";
|
|
11
12
|
import { DARK } from "../theme";
|
|
12
|
-
import { dragText, typeText, pressKey, waitForText } from "../test-support";
|
|
13
|
+
import { allowEventLoopUpdates, dragText, typeText, pressKey, waitForText } from "../test-support";
|
|
13
14
|
|
|
14
15
|
const SAMPLE = "export function add(a: number, b: number) {\n return a + b;\n}\n";
|
|
15
16
|
|
|
@@ -29,7 +30,53 @@ function planSession(): Thread {
|
|
|
29
30
|
|
|
30
31
|
const noop = (): void => {};
|
|
31
32
|
|
|
33
|
+
function colorHex(color: RGBA): string {
|
|
34
|
+
const [red, green, blue] = color.toInts();
|
|
35
|
+
|
|
36
|
+
return "#" + [red, green, blue].map((part) => part.toString(16).padStart(2, "0")).join("");
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
describe("AnnotatableFileView", () => {
|
|
40
|
+
for (const { path, source, keyword } of [
|
|
41
|
+
{ path: "src/add.ts", source: "export const add = 1;", keyword: "export" },
|
|
42
|
+
{ path: "src/add.js", source: "const add = 1;", keyword: "const" },
|
|
43
|
+
]) {
|
|
44
|
+
test(`highlights ${path} by its detected language`, async () => {
|
|
45
|
+
allowEventLoopUpdates();
|
|
46
|
+
const setup = await testRender(
|
|
47
|
+
<AnnotatableFileView
|
|
48
|
+
path={path}
|
|
49
|
+
loadContents={() => Promise.resolve(source)}
|
|
50
|
+
session={planSession()}
|
|
51
|
+
quickActions={[]}
|
|
52
|
+
observer={false}
|
|
53
|
+
onAddComment={noop}
|
|
54
|
+
onReply={noop}
|
|
55
|
+
onUpdateAnnotation={noop}
|
|
56
|
+
onExit={noop}
|
|
57
|
+
theme={DARK}
|
|
58
|
+
/>,
|
|
59
|
+
{ width: 64, height: 12 },
|
|
60
|
+
);
|
|
61
|
+
let highlighted = false;
|
|
62
|
+
|
|
63
|
+
for (let attempt = 0; attempt < 60 && !highlighted; attempt++) {
|
|
64
|
+
await setup.renderOnce();
|
|
65
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
66
|
+
highlighted = setup.captureSpans().lines.some((line) =>
|
|
67
|
+
line.spans
|
|
68
|
+
.filter((span) => colorHex(span.fg) === DARK.accent)
|
|
69
|
+
.map((span) => span.text)
|
|
70
|
+
.join("")
|
|
71
|
+
.includes(keyword),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
expect(highlighted).toBe(true);
|
|
76
|
+
setup.renderer.destroy();
|
|
77
|
+
}, 25000);
|
|
78
|
+
}
|
|
79
|
+
|
|
33
80
|
test("commenting a file line hands up an anchor that quotes the line", async () => {
|
|
34
81
|
const onAddComment = mock((_anchor: Anchor, _body: string) => {});
|
|
35
82
|
const setup = await testRender(
|
package/src/diff-syntax.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import { highlightJobs, spansByLine } from "./diff-syntax";
|
|
3
|
-
import type
|
|
3
|
+
import { fileContentsRows, type DiffRow } from "./view-diff";
|
|
4
4
|
import type { SimpleHighlight } from "@opentui/core";
|
|
5
5
|
|
|
6
6
|
function row(kind: DiffRow["kind"], text: string, file = "src/x.ts"): DiffRow {
|
|
@@ -74,6 +74,19 @@ describe("spansByLine", () => {
|
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
describe("highlightJobs", () => {
|
|
77
|
+
for (const { path, filetype } of [
|
|
78
|
+
{ path: "src/add.ts", filetype: "typescript" },
|
|
79
|
+
{ path: "src/add.py", filetype: "python" },
|
|
80
|
+
{ path: "config.json", filetype: "json" },
|
|
81
|
+
]) {
|
|
82
|
+
test(`detects ${filetype} in a plain project file without a diff header`, () => {
|
|
83
|
+
const source = "first line\nsecond line";
|
|
84
|
+
const rows = fileContentsRows(path, source);
|
|
85
|
+
|
|
86
|
+
expect(highlightJobs(rows)).toEqual([{ filetype, source, rowIndexByLine: [0, 1] }]);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
77
90
|
test("reconstructs old and new sides of a hunk with row indices", () => {
|
|
78
91
|
// Arrange
|
|
79
92
|
const rows = [
|
|
@@ -100,7 +113,7 @@ describe("highlightJobs", () => {
|
|
|
100
113
|
const rows = [
|
|
101
114
|
row("file", "notes/thing.unknownext", "notes/thing.unknownext"),
|
|
102
115
|
row("hunk", "@@"),
|
|
103
|
-
row("add", "whatever"),
|
|
116
|
+
row("add", "whatever", "notes/thing.unknownext"),
|
|
104
117
|
];
|
|
105
118
|
|
|
106
119
|
// Act
|
package/src/diff-syntax.ts
CHANGED
|
@@ -31,9 +31,8 @@ interface HighlightJob {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* Split diff rows into per-hunk highlight jobs, one per side.
|
|
35
|
-
*
|
|
36
|
-
* header rows and files without a known filetype are skipped.
|
|
34
|
+
* Split diff rows into per-hunk highlight jobs, one per side. Plain project
|
|
35
|
+
* files contain only context rows, so detect their language from the row path.
|
|
37
36
|
*/
|
|
38
37
|
export function highlightJobs(rows: DiffRow[]): HighlightJob[] {
|
|
39
38
|
const jobs: HighlightJob[] = [];
|
|
@@ -42,10 +41,12 @@ export function highlightJobs(rows: DiffRow[]): HighlightJob[] {
|
|
|
42
41
|
let oldRowIndexByLine: number[] = [];
|
|
43
42
|
let newRowIndexByLine: number[] = [];
|
|
44
43
|
let filetype: string | undefined;
|
|
44
|
+
let path: string | undefined;
|
|
45
|
+
let hasChange = false;
|
|
45
46
|
|
|
46
47
|
const flush = (): void => {
|
|
47
48
|
if (filetype) {
|
|
48
|
-
if (oldLines.length)
|
|
49
|
+
if (oldLines.length && (hasChange || newLines.length === 0))
|
|
49
50
|
jobs.push({ filetype, source: oldLines.join("\n"), rowIndexByLine: oldRowIndexByLine });
|
|
50
51
|
if (newLines.length)
|
|
51
52
|
jobs.push({ filetype, source: newLines.join("\n"), rowIndexByLine: newRowIndexByLine });
|
|
@@ -54,14 +55,20 @@ export function highlightJobs(rows: DiffRow[]): HighlightJob[] {
|
|
|
54
55
|
newLines = [];
|
|
55
56
|
oldRowIndexByLine = [];
|
|
56
57
|
newRowIndexByLine = [];
|
|
58
|
+
hasChange = false;
|
|
57
59
|
};
|
|
58
60
|
|
|
59
61
|
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
60
62
|
const row = rows[rowIndex]!;
|
|
61
63
|
|
|
64
|
+
if (row.file !== path) {
|
|
65
|
+
flush();
|
|
66
|
+
path = row.file;
|
|
67
|
+
filetype = filetypeForPath(path);
|
|
68
|
+
}
|
|
69
|
+
|
|
62
70
|
if (row.kind === "file" || row.kind === "hunk") {
|
|
63
71
|
flush();
|
|
64
|
-
filetype = row.kind === "file" ? filetypeForPath(row.file) : filetype;
|
|
65
72
|
continue;
|
|
66
73
|
}
|
|
67
74
|
const text = stripTrailingNewline(row.text);
|
|
@@ -74,6 +81,7 @@ export function highlightJobs(rows: DiffRow[]): HighlightJob[] {
|
|
|
74
81
|
newLines.push(text);
|
|
75
82
|
newRowIndexByLine.push(rowIndex);
|
|
76
83
|
}
|
|
84
|
+
if (row.kind === "add" || row.kind === "del") hasChange = true;
|
|
77
85
|
}
|
|
78
86
|
flush();
|
|
79
87
|
|