Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
278d175
fix(desktop): polish Projects navigation and detail presentation
thomaspblock Aug 17, 2026
d1d54c9
chore(desktop): keep Tauri entrypoint within size limit
thomaspblock Aug 17, 2026
04ca3eb
fix(desktop): refine Projects overview and list presentation
thomaspblock Aug 18, 2026
3823617
polish(desktop): detail-page layout cleanup for readme and commit views
thomaspblock Aug 19, 2026
4ee8a2f
refactor(desktop): extract ImageMosaic from markdown.tsx
thomaspblock Aug 19, 2026
4cac268
feat(desktop): open latest project conversation from workspace Channe…
thomaspblock Aug 19, 2026
e9bd8cd
refactor(desktop): extract the latest repository commit row
thomaspblock Aug 19, 2026
e4f53a0
polish(desktop): simplify Projects context chrome and list layout
thomaspblock Aug 20, 2026
c463bc6
Merge origin/main into projects-v6-pt5-visual-cleanups
thomaspblock Aug 20, 2026
bd540d0
fix(ci): align Projects smoke specs with context chrome
thomaspblock Aug 20, 2026
5c6d86b
fix(desktop): align review identity and Channels click coverage (#6429)
thomaspblock Aug 20, 2026
bb565f5
fix(desktop): retain one selected review across Projects chrome (#6429)
thomaspblock Aug 20, 2026
570b198
fix(desktop): keep retained review body visible during refetch (#6429)
thomaspblock Aug 20, 2026
255c8b9
fix(desktop): render real review panel in refetch regression (#6429)
thomaspblock Aug 20, 2026
09e6ca2
Merge remote-tracking branch 'origin/main' into projects-v6-pt5-visua…
thomaspblock Aug 20, 2026
7712bbc
test(desktop): isolate ForumComposer stub from the shared test loader
thomaspblock Aug 20, 2026
71520a8
test(desktop): drive retained review through WorkspaceTabs
thomaspblock Aug 21, 2026
733380e
Merge origin/main into projects-v6-pt5-visual-cleanups
thomaspblock Aug 21, 2026
b8df8c4
fix(desktop): keep mention caret and channel tooltip just-now
thomaspblock Aug 21, 2026
9a98601
fix(desktop): expect composer mention chips to stay inline
thomaspblock Aug 21, 2026
a5eb269
fix(desktop): keep typing after mention chips
thomaspblock Aug 21, 2026
49afb82
fix(desktop): fence mention caret after autocomplete insert
thomaspblock Aug 21, 2026
0071d6f
fix(desktop): settle mention caret in the inserting editor
thomaspblock Aug 21, 2026
ca61bd6
fix(desktop): stop mention settlement from stealing ArrowLeft
thomaspblock Aug 21, 2026
35fc1b6
Merge origin/main into projects-v6-pt5-visual-cleanups
thomaspblock Aug 21, 2026
208209f
fix(desktop): honor ArrowLeft and chip clicks after mentions
thomaspblock Aug 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,6 @@ pub fn run() {
RunEvent::Exit => {
shut_down_app(app_handle, &run_shutdown_done);
app_handle.state::<ClipboardState>().release();

#[cfg(all(feature = "mesh-llm", target_os = "macos"))]
if restart_requested.load(Ordering::SeqCst) {
relaunch_after_mesh_shutdown(app_handle);
Expand Down
148 changes: 148 additions & 0 deletions desktop/src/features/messages/lib/mentionHighlightExtension.test.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import assert from "node:assert/strict";
import test from "node:test";

import { getSchema } from "@tiptap/core";
import StarterKit from "@tiptap/starter-kit";

import {
assignMentionHighlightNames,
buildHighlightPatterns,
createMentionCaretSettlement,
findHighlightMatches,
insertPosForMentionTextInput,
mentionTextInputInsertPos,
positionAfterArrowLeftThroughMentionSpace,
selectionAfterMentionTrailingSpace,
shouldAdvanceMentionCaret,
} from "./mentionHighlightExtension.ts";

// ── buildHighlightPatterns ────────────────────────────────────────────
Expand Down Expand Up @@ -164,3 +174,141 @@ test("#general should NOT match inside #generally (trailing word boundary)", ()
const matches = findHighlightMatches("#generally", patterns);
assert.equal(matches.length, 0);
});

const schema = getSchema([
StarterKit.configure({
heading: false,
trailingNode: false,
link: false,
}),
]);
const paragraph = (...content) => schema.nodes.paragraph.create(null, content);
const text = (value) => schema.text(value);
const document = (...content) => schema.nodes.doc.create(null, content);

test("selectionAfterMentionTrailingSpace steps past the space after @Name", () => {
const doc = document(paragraph(text("@quinn ")));
const spacePos = 1 + "@quinn".length;
assert.equal(selectionAfterMentionTrailingSpace(doc, spacePos), spacePos + 1);
assert.equal(
selectionAfterMentionTrailingSpace(doc, spacePos + 1),
spacePos + 1,
);
});

test("selectionAfterMentionTrailingSpace leaves a caret inside the mention name", () => {
const doc = document(paragraph(text("@quinn ")));
assert.equal(selectionAfterMentionTrailingSpace(doc, 4), 4);
});

test("selectionAfterMentionTrailingSpace does not move without a trailing space", () => {
const doc = document(paragraph(text("@quinn")));
const end = 1 + "@quinn".length;
assert.equal(selectionAfterMentionTrailingSpace(doc, end), end);
});

test("shouldAdvanceMentionCaret restores a remap while this editor is settling", () => {
assert.equal(
shouldAdvanceMentionCaret({
from: 7,
next: 8,
settling: true,
docChanged: false,
}),
true,
);
});

test("shouldAdvanceMentionCaret does not steal ArrowLeft after settlement is cancelled", () => {
assert.equal(
shouldAdvanceMentionCaret({
from: 7,
next: 8,
settling: false,
docChanged: false,
}),
false,
);
});

test("shouldAdvanceMentionCaret still advances after a document change", () => {
assert.equal(
shouldAdvanceMentionCaret({
from: 7,
next: 8,
settling: false,
docChanged: true,
}),
true,
);
});

test("createMentionCaretSettlement keeps two editors independent", () => {
const composerA = createMentionCaretSettlement();
const composerB = createMentionCaretSettlement();
composerA.arm(8);
assert.equal(composerB.peek(), null);
composerB.arm(12);
composerA.cancel();
assert.equal(composerA.peek(), null);
assert.equal(composerB.peek(), 12);
});

test("insertPosForMentionTextInput redirects a caret at the chip edge", () => {
const doc = document(paragraph(text("@quinn ")));
const spacePos = 1 + "@quinn".length;
assert.equal(
insertPosForMentionTextInput(doc, spacePos, spacePos),
spacePos + 1,
);
assert.equal(
insertPosForMentionTextInput(doc, spacePos + 1, spacePos + 1),
null,
);
});

test("insertPosForMentionTextInput keeps a selected trailing space", () => {
const doc = document(paragraph(text("@quinn ")));
const spacePos = 1 + "@quinn".length;
assert.equal(
insertPosForMentionTextInput(doc, spacePos, spacePos + 1),
spacePos + 1,
);
});

test("mentionTextInputInsertPos honors a deliberate caret after settlement", () => {
const doc = document(paragraph(text("@bob ")));
const spacePos = 1 + "@bob".length;
assert.equal(mentionTextInputInsertPos(doc, spacePos, spacePos, false), null);
assert.equal(
mentionTextInputInsertPos(doc, spacePos, spacePos, true),
spacePos + 1,
);
});

test("positionAfterArrowLeftThroughMentionSpace steps onto the token end", () => {
const doc = document(paragraph(text("@bob ")));
const afterSpace = 1 + "@bob".length + 1;
assert.equal(
positionAfterArrowLeftThroughMentionSpace(doc, afterSpace),
afterSpace - 1,
);
assert.equal(
positionAfterArrowLeftThroughMentionSpace(doc, afterSpace - 1),
null,
);
});

test("assignMentionHighlightNames skips an unchanged list", () => {
const storage = { names: ["bob"], agentNames: [], channelNames: [] };
assert.equal(assignMentionHighlightNames(storage, ["bob"], [], []), false);
});

test("assignMentionHighlightNames updates when a new mention is added", () => {
const storage = { names: ["bob"], agentNames: [], channelNames: [] };
assert.equal(
assignMentionHighlightNames(storage, ["bob", "quinn"], [], []),
true,
);
assert.deepEqual(storage.names, ["bob", "quinn"]);
});
Loading
Loading