Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion apps/extension/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dockmark/extension",
"version": "1.5.3",
"version": "1.5.4",
"private": true,
"type": "module",
"scripts": {
Expand Down
28 changes: 23 additions & 5 deletions apps/extension/public/newtab-parity.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
(() => {
const LAUNCHER_RESULT_LIMIT = 8;
const SOURCE_WEIGHT = {
tab: 500,
workspace: 400,
Expand Down Expand Up @@ -64,6 +65,18 @@
};
}

function regularSearchFallback(query) {
const engine = preferredSearchEngine();
const keyword = typeof engine?.keyword === "string" ? engine.keyword.trim() : "";
return {
kind: "search",
title: `Search ${engine?.name || "the web"} for “${query}”`,
subtitle: keyword ? `Default · !${keyword}` : "Default search engine",
url: searchUrl(engine, query),
score: SOURCE_WEIGHT.search,
};
}

function sessionResult(query, session, index) {
const items = asArray(session?.items).filter((item) => isHttpUrl(item?.url));
const sourceDevice = typeof session?.sourceDevice === "string" ? session.sourceDevice.trim() : "";
Expand Down Expand Up @@ -92,8 +105,9 @@
if (!query) return [];

const existing = originalBuildResults(rawQuery);
if (query.startsWith("@") || searchEngineCommand(query) || (existing.length && existing.every((result) => result.kind === "search-shortcut"))) {
return existing;
if (query.startsWith("@") || searchEngineCommand(query)) return existing;
if (existing.length && existing.every((result) => result.kind === "search-shortcut")) {
return existing.slice(0, LAUNCHER_RESULT_LIMIT);
}

const deduped = new Map();
Expand All @@ -114,9 +128,13 @@
if (result) deduped.set(`session:${session?.id || result.title}`, result);
});

return [...deduped.values()]
.sort((left, right) => right.score - left.score || String(left.title || "").localeCompare(String(right.title || "")))
.slice(0, 10);
const ranked = [...deduped.values()]
.sort((left, right) => right.score - left.score || String(left.title || "").localeCompare(String(right.title || "")));

return [
...ranked.filter((result) => result.kind !== "search").slice(0, LAUNCHER_RESULT_LIMIT - 1),
regularSearchFallback(query),
];
};

renderSearchResults = function parityRenderSearchResults() {
Expand Down
2 changes: 1 addition & 1 deletion apps/extension/wxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default defineConfig({
modules: ["@wxt-dev/module-react"],
manifest: ({ browser }) => ({
name: "Dockmark",
version: "1.5.3",
version: "1.5.4",
description: "Browser bridge for Dockmark tabs, reviewed Capture Inbox, sessions, Smart Collection-aware local-first search, reviewed bookmark sync, local health checks and device pairing.",
icons: {
16: "icons/dockmark-16.png",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/extension-distribution.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const EXTENSION_RELEASE_VERSION = "1.5.3";
export const EXTENSION_RELEASE_VERSION = "1.5.4";

// Fill these when browser-store listings are public. The UI will promote the
// store action while keeping manual downloads visible.
Expand Down
34 changes: 34 additions & 0 deletions scripts/chromium-newtab-parity-smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,39 @@ try {
"Shared Match Bookmark",
]);

await page.evaluate(() => {
const now = new Date().toISOString();
snapshot = {
...snapshot,
bookmarks: Array.from({ length: 12 }, (_, index) => ({
id: `overflow-bookmark-${index + 1}`,
title: `Overflow Match Bookmark ${index + 1}`,
url: `https://overflow.example/${index + 1}`,
description: "Overflow Match fixture",
tags: [],
healthPolicy: "normal",
healthStatus: "unknown",
position: index,
createdAt: now,
updatedAt: now,
})),
};
openTabs = [];
activeResult = 0;
elements.search.value = "Overflow Match";
renderSearchResults();
});

const overflowKinds = (await page.locator("#command-results .result-kind").allInnerTexts())
.map((value) => value.toLocaleLowerCase());
assert.equal(overflowKinds.length, 8, "New Tab should expose the same eight-result Launcher cap as Web.");
assert.deepEqual(overflowKinds.slice(0, 7), Array(7).fill("bookmark"));
assert.equal(overflowKinds[7], "search", "Default Search must remain visible even when local matches exceed the result limit.");
assert.match(
await page.locator("#command-results .result-row").last().innerText(),
/Search Google for “Overflow Match”/i,
);

const search = page.locator("#search");
await search.fill("workspace-only-item");
const workspaceRow = page.locator("#command-results .result-row").filter({ hasText: "Shared Match Workspace" }).first();
Expand All @@ -147,6 +180,7 @@ try {
assert.equal(await search.evaluate((element) => document.activeElement === element), false, "Escape on an empty Launcher should blur it on both Web and New Tab.");

console.log("✓ New Tab canonical ordering is Open tab > Workspace > Session > Collection > Bookmark > Search");
console.log("✓ New Tab keeps the eight-result Web Launcher cap and reserves the final slot for Search");
console.log("✓ New Tab exposes the same human-readable result type labels as Web Launcher");
console.log("✓ Workspace item title/URL content participates in New Tab Launcher search");
console.log("✓ Escape clears first and blurs the empty Launcher consistently");
Expand Down
2 changes: 1 addition & 1 deletion scripts/chromium-newtab-smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ try {
assert.equal(await page.locator("#command-results .result-row").filter({ hasText: "Cached Example" }).count(), 0);

await page.locator("#search").fill("offline query");
await page.getByText("Search Secondary Search", { exact: true }).waitFor();
await page.getByText("Search Secondary Search for “offline query”", { exact: true }).waitFor();

await page.locator("#search").fill("e offline query");
await page.getByText("Search Example Search", { exact: true }).waitFor();
Expand Down
Loading