Compare commits

..

2 Commits

Author SHA1 Message Date
mr. m
1af79e8cc8 no-bug: Format 2026-09-18 12:23:18 +02:00
mr. m
ff4bb1a72d closes #15426: Fixed spaces wiping if zen is opened on other devices 2026-09-18 12:21:57 +02:00
5 changed files with 77 additions and 22 deletions

View File

@@ -39,3 +39,4 @@ zen-action-switch-to-dark-mode = Switch to Dark Mode
zen-action-print = Print
zen-action-focus-on = Focus on
zen-action-extension = Extension
zen-action-open = Open

View File

@@ -164,12 +164,11 @@
}
#navigator-toolbox[zen-has-implicit-hover="true"] &,
&:is([open], [showing]),
&[open],
#urlbar[has-popup-open="true"] &,
:root[zen-has-empty-tab="true"] & {
opacity: 1;
visibility: visible;
transition: opacity 0.15s !important;
}
}
}
@@ -653,11 +652,10 @@
--urlbarview-row-padding-block: 10px;
color: light-dark(rgba(0, 0, 0, 0.7), rgba(255, 255, 255, 0.7)) !important;
&:hover {
&,
& .urlbarView-favicon {
background-color: color-mix(in srgb, var(--zen-branding-bg-reverse) 5%, transparent 95%) !important;
}
&:hover,
&:hover .urlbarView-favicon,
.urlbarView-action-favicon {
background-color: color-mix(in srgb, var(--zen-branding-bg-reverse) 5%, transparent 95%) !important;
}
&[selected] {

View File

@@ -145,6 +145,22 @@ export function recordDigest(kind, data) {
class nsZenSpacesSyncModel {
#file = null;
#cache = null;
/**
* Ids applied from an incoming batch, keyed to the projection generation
* (sidebar lastCollected) that was current when they were applied. The
* projection only refreshes on the delayed session collection that runs
* after the sync, so within the same sync the just-applied ids are in the
* uploaded snapshot (noteApplied) but not yet in the projection. Without
* this, the outgoing diff would read that gap as local deletions and
* upload tombstones for records it just downloaded, wiping them on every
* other device (gh-15426).
*/
#appliedStamp = new Map();
/** The projection generation the current sidebar data belongs to. */
#currentStamp() {
return lazy.ZenSessionStore.getSidebarData()?.lastCollected || 0;
}
#data() {
if (!this.#file) {
@@ -743,6 +759,7 @@ class nsZenSpacesSyncModel {
const uploaded = this.#data().uploaded;
const current = this.#digestAll();
const pending = this.#pendingIds();
const stamp = this.#currentStamp();
const now = Date.now() / 1000;
const changes = {};
for (const [id, digest] of current) {
@@ -751,7 +768,11 @@ class nsZenSpacesSyncModel {
}
}
for (const id of Object.keys(uploaded)) {
if (!current.has(id) && !pending.has(id)) {
if (
!current.has(id) &&
!pending.has(id) &&
this.#appliedStamp.get(id) !== stamp
) {
changes[id] = now;
}
}
@@ -774,13 +795,18 @@ class nsZenSpacesSyncModel {
const uploaded = this.#data().uploaded;
const current = this.#digestAll();
const pending = this.#pendingIds();
const stamp = this.#currentStamp();
for (const [id, digest] of current) {
if (uploaded[id] !== digest) {
return true;
}
}
for (const id of Object.keys(uploaded)) {
if (!current.has(id) && !pending.has(id)) {
if (
!current.has(id) &&
!pending.has(id) &&
this.#appliedStamp.get(id) !== stamp
) {
return true;
}
}
@@ -825,8 +851,12 @@ class nsZenSpacesSyncModel {
const data = this.#data();
if (!cleartext) {
delete data.uploaded[id];
this.#appliedStamp.delete(id);
} else {
data.uploaded[id] = recordDigest(cleartext.kind, cleartext.data);
// Hold back a tombstone for this id until the projection is recollected
// past the generation it was applied in (gh-15426).
this.#appliedStamp.set(id, this.#currentStamp());
}
syncLog(
`acknowledged incoming ${cleartext ? cleartext.kind : "tombstone"} ${id}`

View File

@@ -302,6 +302,10 @@ const globalActionsTemplate = [
},
];
export function formatValueSync(l10nId) {
return lazy.l10n.formatValueSync(l10nId);
}
export const globalActions = globalActionsTemplate.map(action => ({
isAvailable: window => {
return (
@@ -317,6 +321,6 @@ export const globalActions = globalActionsTemplate.map(action => ({
extraPayload: {},
...action,
get label() {
return lazy.l10n.formatValueSync(action.l10nId);
return formatValueSync(action.l10nId);
},
}));

View File

@@ -7,6 +7,7 @@ import {
UrlbarUtils,
} from "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs";
import { UrlbarShared } from "chrome://browser/content/urlbar/UrlbarShared.mjs";
import { formatValueSync } from "resource:///modules/ZenUBGlobalActions.sys.mjs";
const lazy = {};
@@ -34,7 +35,6 @@ export class ZenUrlbarProviderSidebar extends UrlbarProvider {
}
async isActive(queryContext) {
// The sidebar data never holds tabs from private windows.
return (
!queryContext.isPrivate &&
!!queryContext.tokens.length &&
@@ -49,12 +49,18 @@ export class ZenUrlbarProviderSidebar extends UrlbarProvider {
return;
}
const tokens = queryContext.tokens.map(t => t.lowerCaseValue);
const matches = text => {
const matches = (percentage, text) => {
text = text.toLowerCase();
return tokens.every(token => text.includes(token));
const matchCount = tokens.filter(token => text.includes(token)).length;
return matchCount / tokens.length >= percentage;
};
this.#addFolders(sidebar, matches, addCallback);
this.#addTabs(sidebar, matches, queryContext, addCallback);
this.#addFolders(sidebar, matches.bind(undefined, 0.7), addCallback);
this.#addTabs(
sidebar,
matches.bind(undefined, 0.4),
queryContext,
addCallback
);
}
#addTabs(sidebar, matches, queryContext, addCallback) {
@@ -123,7 +129,7 @@ export class ZenUrlbarProviderSidebar extends UrlbarProvider {
continue;
}
const space = sidebar.spaces?.find(s => s.uuid == folder.workspaceId);
const path = [];
const path = [folder.name];
for (
let parent = folders.get(folder.parentId);
parent;
@@ -142,9 +148,9 @@ export class ZenUrlbarProviderSidebar extends UrlbarProvider {
payload: {
dynamicType: DYNAMIC_TYPE_NAME,
zenFolderId: folder.id,
title: folder.name,
icon:
folder.userIcon || "chrome://browser/skin/zen-icons/folder.svg",
titleL10n: "zen-action-open",
userIcon: folder.userIcon,
icon: "chrome://browser/skin/zen-icons/folder.svg",
path: path.join(" / "),
},
})
@@ -156,13 +162,19 @@ export class ZenUrlbarProviderSidebar extends UrlbarProvider {
return {
icon: { attributes: { src: result.payload.icon } },
titleStrong: {
textContent: result.payload.title,
textContent: formatValueSync(result.payload.titleL10n),
attributes: { dir: "ltr" },
},
path: {
textContent: result.payload.path,
attributes: { dir: "ltr", hidden: !result.payload.path },
},
userIcon: {
attributes: {
src: result.payload.userIcon,
hidden: !result.payload.userIcon,
},
},
};
}
@@ -175,7 +187,7 @@ export class ZenUrlbarProviderSidebar extends UrlbarProvider {
{
name: "icon",
tag: "img",
classList: ["urlbarView-favicon"],
classList: ["urlbarView-favicon", "urlbarView-action-favicon"],
},
{
name: "title",
@@ -189,9 +201,19 @@ export class ZenUrlbarProviderSidebar extends UrlbarProvider {
],
},
{
name: "path",
tag: "span",
classList: ["urlbarView-prettyName"],
children: [
{
name: "userIcon",
tag: "img",
attributes: { hidden: true },
},
{
name: "path",
tag: "span",
},
],
},
],
};