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.1",
"version": "1.5.2",
"private": true,
"type": "module",
"scripts": {
Expand Down
38 changes: 33 additions & 5 deletions apps/extension/public/newtab-ux.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,42 @@
};
}

function queryTerms(value) {
return String(value || "").trim().toLocaleLowerCase().split(/\s+/).filter(Boolean);
}

function valuesMatchQuery(query, values) {
const terms = queryTerms(query);
if (!terms.length) return true;
const haystack = values.map((value) => String(value || "")).join("\n").toLocaleLowerCase();
return terms.every((term) => haystack.includes(term));
}

function matchingSessionItem(query, items) {
return items.find((item) => valuesMatchQuery(query, [item?.title, item?.url])) || null;
}

function sessionResult(query, session, index) {
const items = asArray(session?.items).filter((item) => isHttpUrl(item?.url));
const source = typeof session?.sourceDevice === "string" && session.sourceDevice.trim()
? ` · ${session.sourceDevice.trim()}`
const sourceDevice = typeof session?.sourceDevice === "string" ? session.sourceDevice.trim() : "";
const searchValues = [
session?.name,
sourceDevice,
...items.flatMap((item) => [item?.title, item?.url]),
];
if (!valuesMatchQuery(query, searchValues)) return null;

const matchedItem = matchingSessionItem(query, items);
const source = sourceDevice ? ` · ${sourceDevice}` : "";
const itemMatch = matchedItem
? ` · ${matchedItem.title || displayHost(matchedItem.url)} · ${matchedItem.url}`
: "";
const subtitle = `${items.length} tab${items.length === 1 ? "" : "s"}${source}`;
const score = searchScore(query, session?.name, subtitle, 390);
if (score < 0) return null;
const subtitle = `${items.length} tab${items.length === 1 ? "" : "s"}${source}${itemMatch}`;

let score = searchScore(query, session?.name, sourceDevice, 430);
if (score < 0 && matchedItem) score = searchScore(query, matchedItem?.title, matchedItem?.url, 400);
if (score < 0) score = 390;

return {
kind: "session",
title: session.name || "Session",
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.1",
version: "1.5.2",
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.1";
export const EXTENSION_RELEASE_VERSION = "1.5.2";

// Fill these when browser-store listings are public. The UI will promote the
// store action while keeping manual downloads visible.
Expand Down
65 changes: 48 additions & 17 deletions scripts/chromium-newtab-ux-smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -171,21 +171,38 @@ try {
}
if (requestUrl.pathname === "/api/sessions") {
return json({
sessions: [{
id: "session-ux",
name: "Morning Session",
sourceDevice: "Smoke browser",
createdAt: now,
updatedAt: now,
items: [{
id: "session-item-ux",
sessionId: "session-ux",
title: "Session target",
url: `${origin}/session`,
pinned: false,
position: 0,
}],
}],
sessions: [
{
id: "session-ux",
name: "Morning Session",
sourceDevice: "Smoke browser",
createdAt: now,
updatedAt: now,
items: [{
id: "session-item-ux",
sessionId: "session-ux",
title: "Session target",
url: `${origin}/session`,
pinned: false,
position: 0,
}],
},
{
id: "session-campus",
name: "Campus Tools",
sourceDevice: "Web",
createdAt: now,
updatedAt: now,
items: [{
id: "session-item-campus",
sessionId: "session-campus",
title: "学生电子邮箱服务中心 — 北京信息科技大学 BIST",
url: "https://mail.bist.edu.cn/mail/",
pinned: false,
position: 0,
}],
},
],
});
}
return json({});
Expand Down Expand Up @@ -219,11 +236,24 @@ try {
assert.match(await sessionRow.innerText(), /1 tab/);
assert.match(await sessionRow.innerText(), /Smoke browser/);

await search.fill("学生电子");
const campusByTitle = page.locator("#command-results .result-row").filter({ hasText: "Campus Tools" }).first();
await campusByTitle.waitFor();
assert.match(await campusByTitle.innerText(), /学生电子邮箱服务中心/);

await search.fill("mail.bist");
const campusByUrl = page.locator("#command-results .result-row").filter({ hasText: "Campus Tools" }).first();
await campusByUrl.waitFor();
assert.match(await campusByUrl.innerText(), /mail\.bist\.edu\.cn\/mail/);

await search.fill("bist.edu.cn/mail");
await page.locator("#command-results .result-row").filter({ hasText: "Campus Tools" }).first().waitFor();

const cachedSessionNames = await page.evaluate(async () => {
const stored = await chrome.storage.local.get("dockmarkNewTabCacheV1");
return (stored.dockmarkNewTabCacheV1?.sessions ?? []).map((session) => session.name);
});
assert.deepEqual(cachedSessionNames, ["Morning Session"]);
assert.deepEqual(cachedSessionNames, ["Morning Session", "Campus Tools"]);

await search.fill("Cached Target");
const foregroundPromise = context.waitForEvent("page");
Expand Down Expand Up @@ -254,7 +284,8 @@ try {

console.log("✓ New Tab suggests bare ! search-engine shortcuts and Enter accepts them");
console.log("✓ New Tab auto refresh hydrates and caches Sessions on the first real launcher load");
console.log("✓ New Tab searches and restores a cloud-refreshed Session");
console.log("✓ New Tab searches Sessions by name, tab title and partial tab URL");
console.log("✓ New Tab restores a cloud-refreshed Session");
console.log("✓ Shift+Enter opens a selected URL in a foreground tab");
console.log("✓ Ctrl/Command+Enter opens a selected URL in a background tab");
console.log("✓ / focuses the launcher input from the New Tab page");
Expand Down
Loading