Repository: toeverything/AFFiNE
Pull request #12296: feat(core): edit and delete pinned collections in all docs
Author: login: EYHN
Labels: name: app:core
Created: 2025-05-15T04:02:18Z | Merged: 2025-05-19T04:38:31Z



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Added the ability to edit and remove pinned collections directly from the workspace UI.
  - Improved filter management with clearer handling of temporary filters and editing workflows.
  - Enhanced synchronization and readiness tracking for collections and pinned collections, resulting in more responsive and reliable updates.

- **Style**
  - Updated pinned collection item styles for better interaction feedback, including new edit and remove button visuals.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx b/packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx
index 419d5e6641140..f7b6cb70d9ff2 100644
--- a/packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx
+++ b/packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx
@@ -35,6 +35,18 @@ export const AllPage = () => {
   const collectionService = useService(CollectionService);
   const pinnedCollectionService = useService(PinnedCollectionService);
 
+  const isCollectionDataReady = useLiveData(
+    collectionService.collectionDataReady$
+  );
+
+  const isPinnedCollectionDataReady = useLiveData(
+    pinnedCollectionService.pinnedCollectionDataReady$
+  );
+
+  const pinnedCollections = useLiveData(
+    pinnedCollectionService.pinnedCollections$
+  );
+
   const [selectedCollectionId, setSelectedCollectionId] = useState<
     string | null
   >(null);
@@ -44,18 +56,29 @@ export const AllPage = () => {
       : null
   );
 
+  useEffect(() => {
+    // if selected collection is not in pinned collections, set selected collection id to null
+    if (
+      isPinnedCollectionDataReady &&
+      selectedCollectionId &&
+      !pinnedCollections.some(c => c.collectionId === selectedCollectionId)
+    ) {
+      setSelectedCollectionId(null);
+    }
+  }, [isPinnedCollectionDataReady, pinnedCollections, selectedCollectionId]);
+
   useEffect(() => {
     // if selected collection is not found, set selected collection id to null
-    if (!selectedCollection && selectedCollectionId) {
+    if (!selectedCollection && selectedCollectionId && isCollectionDataReady) {
       setSelectedCollectionId(null);
     }
-  }, [selectedCollection, selectedCollectionId]);
+  }, [isCollectionDataReady, selectedCollection, selectedCollectionId]);
 
   const selectedCollectionInfo = useLiveData(
     selectedCollection ? selectedCollection.info$ : null
   );
 
-  const [tempFilters, setTempFilters] = useState<FilterParams[]>([]);
+  const [tempFilters, setTempFilters] = useState<FilterParams[] | null>(null);
 
   const [explorerContextValue] = useState(createDocExplorerContext);
 
@@ -68,10 +91,9 @@ export const AllPage = () => {
   useEffect(() => {
     const subscription = collectionRulesService
       .watch(
-        // collection filters and temp filters can't exist at the same time
         selectedCollectionInfo
           ? {
-              filters: selectedCollectionInfo.rules.filters,
+              filters: tempFilters ?? selectedCollectionInfo.rules.filters,
               groupBy,
               orderBy,
               extraAllowList: selectedCollectionInfo.allowList,
@@ -160,42 +182,74 @@ export const AllPage = () => {
     setTempFilters(filters);
   }, []);
 
+  const handleSelectCollection = useCallback((collectionId: string) => {
+    setSelectedCollectionId(collectionId);
+    setTempFilters(null);
+  }, []);
+
+  const handleEditCollection = useCallback(
+    (collectionId: string) => {
+      const collection = collectionService.collection$(collectionId).value;
+      if (!collection) {
+        return;
+      }
+      setSelectedCollectionId(collectionId);
+      setTempFilters(collection.info$.value.rules.filters);
+    },
+    [collectionService]
+  );
+
   const handleSaveFilters = useCallback(() => {
-    openPromptModal({
-      title: t['com.affine.editCollection.saveCollection'](),
-      label: t['com.affine.editCollectionName.name'](),
-      inputOptions: {
-        placeholder: t['com.affine.editCollectionName.name.placeholder'](),
-      },
-      children: t['com.affine.editCollectionName.createTips'](),
-      confirmText: t['com.affine.editCollection.save'](),
-      cancelText: t['com.affine.editCollection.button.cancel'](),
-      confirmButtonOptions: {
-        variant: 'primary',
-      },
-      onConfirm(name) {
-        const id = collectionService.createCollection({
-          name,
-          rules: {
-            filters: tempFilters,
-          },
-        });
-        pinnedCollectionService.addPinnedCollection({
-          collectionId: id,
-          index: pinnedCollectionService.indexAt('after'),
-        });
-        setTempFilters([]);
-        setSelectedCollectionId(id);
-      },
-    });
+    if (selectedCollectionId) {
+      collectionService.updateCollection(selectedCollectionId, {
+        rules: {
+          filters: tempFilters ?? [],
+        },
+      });
+      setTempFilters(null);
+    } else {
+      openPromptModal({
+        title: t['com.affine.editCollection.saveCollection'](),
+        label: t['com.affine.editCollectionName.name'](),
+        inputOptions: {
+          placeholder: t['com.affine.editCollectionName.name.placeholder'](),
+        },
+        children: t['com.affine.editCollectionName.createTips'](),
+        confirmText: t['com.affine.editCollection.save'](),
+        cancelText: t['com.affine.editCollection.button.cancel'](),
+        confirmButtonOptions: {
+          variant: 'primary',
+        },
+        onConfirm(name) {
+          const id = collectionService.createCollection({
+            name,
+            rules: {
+              filters: tempFilters ?? [],
+            },
+          });
+          pinnedCollectionService.addPinnedCollection({
+            collectionId: id,
+            index: pinnedCollectionService.indexAt('after'),
+          });
+          setTempFilters(null);
+          setSelectedCollectionId(id);
+        },
+      });
+    }
   }, [
     collectionService,
     openPromptModal,
     pinnedCollectionService,
+    selectedCollectionId,
     t,
     tempFilters,
   ]);
 
+  const handleNewTempFilter = useCallback((params: FilterParams) => {
+    setSelectedCollectionId(null);
+    setTempFilters([params]);
+  }, []);
+
   return (
     <DocExplorerContext.Provider value={explorerContextValue}>
       <ViewTitle title={t['All pages']()} />
@@ -209,29 +263,24 @@ export const AllPage = () => {
           <div className={styles.pinnedCollection}>
             <PinnedCollections
               activeCollectionId={selectedCollectionId}
-              onClickAll={() => setSelectedCollectionId(null)}
-              onClickCollection={collectionId => {
-                setSelectedCollectionId(collectionId);
-                setTempFilters([]);
-              }}
-              onAddFilter={params => {
-                setSelectedCollectionId(null);
-                setTempFilters([...(tempFilters ?? []), params]);
-              }}
-              hiddenAdd={tempFilters.length > 0}
+              onActiveAll={() => setSelectedCollectionId(null)}
+              onActiveCollection={handleSelectCollection}
+              onAddFilter={handleNewTempFilter}
+              onEditCollection={handleEditCollection}
+              hiddenAdd={tempFilters !== null}
             />
           </div>
-          {tempFilters.length > 0 && (
+          {tempFilters !== null && (
             <div className={styles.filterArea}>
               <Filters
                 className={styles.filters}
-                filters={tempFilters ?? []}
+                filters={tempFilters}
                 onChange={handleFilterChange}
               />
               <Button
                 variant="plain"
                 onClick={() => {
-                  setTempFilters([]);
+                  setTempFilters(null);
                 }}
               >
                 {t['Cancel']()}
diff --git a/packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.css.ts b/packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.css.ts
index eb172dc72e151..8cfeb4422fdc2 100644
--- a/packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.css.ts
+++ b/packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.css.ts
@@ -27,6 +27,38 @@ export const item = style({
   },
 });
 
+export const itemContent = style({
+  display: 'inline-block',
+  overflow: 'hidden',
+  whiteSpace: 'nowrap',
+  textOverflow: 'ellipsis',
+  textAlign: 'center',
+  maxWidth: '128px',
+  minWidth: '32px',
+
+  selectors: {
+    [`${item}:hover > &`]: {
+      mask:
+        'linear-gradient(#fff) left / calc(100% - 32px) no-repeat,' +
+        'linear-gradient(90deg,#fff 0%,transparent 50%,transparent 100%) right / 32px no-repeat',
+    },
+  },
+});
+
+export const editIconButton = style({
+  opacity: 0,
+  marginLeft: -16,
+  backgroundColor: cssVarV2('layer/background/hoverOverlay'),
+
+  selectors: {
+    [`${item}:hover > &`]: {
+      opacity: 1,
+    },
+  },
+});
+
+export const closeButton = style({});
+
 export const container = style({
   display: 'flex',
   flexDirection: 'row',
diff --git a/packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.tsx b/packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.tsx
index 782147730e0d8..a1f04cc5c4dd7 100644
--- a/packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.tsx
+++ b/packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.tsx
@@ -7,7 +7,13 @@ import {
 } from '@affine/core/modules/collection';
 import type { FilterParams } from '@affine/core/modules/collection-rules';
 import { useI18n } from '@affine/i18n';
-import { CollectionsIcon, FilterIcon, PlusIcon } from '@blocksuite/icons/rc';
+import {
+  CloseIcon,
+  CollectionsIcon,
+  EditIcon,
+  FilterIcon,
+  PlusIcon,
+} from '@blocksuite/icons/rc';
 import { useLiveData, useService } from '@toeverything/infra';
 import { useMemo, useState } from 'react';
 
@@ -17,10 +23,14 @@ export const PinnedCollectionItem = ({
   record,
   isActive,
   onClick,
+  onClickRemove,
+  onClickEdit,
 }: {
   record: PinnedCollectionRecord;
+  onClickRemove: () => void;
   isActive: boolean;
   onClick: () => void;
+  onClickEdit: () => void;
 }) => {
   const t = useI18n();
   const collectionService = useService(CollectionService);
@@ -38,22 +48,46 @@ export const PinnedCollectionItem = ({
       data-active={isActive ? 'true' : undefined}
       onClick={onClick}
     >
-      {name ?? t['Untitled']()}
+      <span className={styles.itemContent}>{name ?? t['Untitled']()}</span>
+      <IconButton
+        size="16"
+        className={styles.editIconButton}
+        onClick={e => {
+          e.stopPropagation();
+          onClickEdit();
+        }}
+      >
+        <EditIcon />
+      </IconButton>
+      {isActive && (
+        <IconButton
+          className={styles.closeButton}
+          size="16"
+          onClick={e => {
+            e.stopPropagation();
+            onClickRemove();
+          }}
+        >
+          <CloseIcon />
+        </IconButton>
+      )}
     </div>
   );
 };
 
 export const PinnedCollections = ({
   activeCollectionId,
-  onClickAll,
-  onClickCollection,
+  onActiveAll,
+  onActiveCollection,
   onAddFilter,
+  onEditCollection,
   hiddenAdd,
 }: {
   activeCollectionId: string | null;
-  onClickAll: () => void;
-  onClickCollection: (collectionId: string) => void;
+  onActiveAll: () => void;
+  onActiveCollection: (collectionId: string) => void;
   onAddFilter: (params: FilterParams) => void;
+  onEditCollection: (collectionId: string) => void;
   hiddenAdd?: boolean;
 }) => {
   const t = useI18n();
@@ -74,22 +108,32 @@ export const PinnedCollections = ({
       <div
         className={styles.item}
         data-active={activeCollectionId === null ? 'true' : undefined}
-        onClick={onClickAll}
+        onClick={onActiveAll}
         role="button"
       >
         {t['com.affine.all-docs.pinned-collection.all']()}
       </div>
-      {pinnedCollections.map(record => (
+      {pinnedCollections.map((record, index) => (
         <PinnedCollectionItem
           key={record.collectionId}
           record={record}
           isActive={activeCollectionId === record.collectionId}
-          onClick={() => onClickCollection(record.collectionId)}
+          onClick={() => onActiveCollection(record.collectionId)}
+          onClickEdit={() => onEditCollection(record.collectionId)}
+          onClickRemove={() => {
+            const nextCollectionId = pinnedCollections[index - 1]?.collectionId;
+            if (nextCollectionId) {
+              onActiveCollection(nextCollectionId);
+            } else {
+              onActiveAll();
+            }
+            pinnedCollectionService.removePinnedCollection(record.collectionId);
+          }}
         />
       ))}
       {!hiddenAdd && (
         <AddPinnedCollection
-          onAddPinnedCollection={handleAddPinnedCollection}
+          onPinCollection={handleAddPinnedCollection}
           onAddFilter={onAddFilter}
         />
       )}
@@ -98,17 +142,17 @@ export const PinnedCollections = ({
 };
 
 export const AddPinnedCollection = ({
-  onAddPinnedCollection,
+  onPinCollection,
   onAddFilter,
 }: {
-  onAddPinnedCollection: (collectionId: string) => void;
+  onPinCollection: (collectionId: string) => void;
   onAddFilter: (params: FilterParams) => void;
 }) => {
   return (
     <Menu
       items={
         <AddPinnedCollectionMenuContent
-          onAddPinnedCollection={onAddPinnedCollection}
+          onPinCollection={onPinCollection}
           onAddFilter={onAddFilter}
         />
       }
@@ -121,10 +165,10 @@ export const AddPinnedCollection = ({
 };
 
 export const AddPinnedCollectionMenuContent = ({
-  onAddPinnedCollection,
+  onPinCollection,
   onAddFilter,
 }: {
-  onAddPinnedCollection: (collectionId: string) => void;
+  onPinCollection: (collectionId: string) => void;
   onAddFilter: (params: FilterParams) => void;
 }) => {
   const [addingFilter, setAddingFilter] = useState<boolean>(false);
@@ -167,7 +211,7 @@ export const AddPinnedCollectionMenuContent = ({
           prefixIcon={<CollectionsIcon />}
           suffixIcon={<PlusIcon />}
           onClick={() => {
-            onAddPinnedCollection(meta.id);
+            onPinCollection(meta.id);
           }}
         >
           {meta.name ?? t['Untitled']()}
diff --git a/packages/frontend/core/src/modules/collection/services/collection.ts b/packages/frontend/core/src/modules/collection/services/collection.ts
index d511a59adf6ec..88d08218b7c96 100644
--- a/packages/frontend/core/src/modules/collection/services/collection.ts
+++ b/packages/frontend/core/src/modules/collection/services/collection.ts
@@ -19,6 +19,11 @@ export class CollectionService extends Service {
     },
   });
 
+  readonly collectionDataReady$ = LiveData.from(
+    this.store.watchCollectionDataReady(),
+    false
+  );
+
   // collection metas used in collection list, only include `id` and `name`, without `rules` and `allowList`
   readonly collectionMetas$ = LiveData.from(
     this.store.watchCollectionMetas(),
diff --git a/packages/frontend/core/src/modules/collection/services/pinned-collection.ts b/packages/frontend/core/src/modules/collection/services/pinned-collection.ts
index 495ebbde3c19b..6ade90b7393c4 100644
--- a/packages/frontend/core/src/modules/collection/services/pinned-collection.ts
+++ b/packages/frontend/core/src/modules/collection/services/pinned-collection.ts
@@ -14,6 +14,11 @@ export class PinnedCollectionService extends Service {
     super();
   }
 
+  pinnedCollectionDataReady$ = LiveData.from(
+    this.pinnedCollectionStore.watchPinnedCollectionDataReady(),
+    false
+  );
+
   pinnedCollections$ = LiveData.from<PinnedCollectionRecord[]>(
     this.pinnedCollectionStore.watchPinnedCollections(),
     []
diff --git a/packages/frontend/core/src/modules/collection/stores/collection.ts b/packages/frontend/core/src/modules/collection/stores/collection.ts
index da07aefab676c..622ef4f9ca2c2 100644
--- a/packages/frontend/core/src/modules/collection/stores/collection.ts
+++ b/packages/frontend/core/src/modules/collection/stores/collection.ts
@@ -7,7 +7,7 @@ import {
 } from '@toeverything/infra';
 import dayjs from 'dayjs';
 import { nanoid } from 'nanoid';
-import { map, type Observable, switchMap } from 'rxjs';
+import { distinctUntilChanged, map, type Observable, switchMap } from 'rxjs';
 import { Array as YArray } from 'yjs';
 
 import type { FilterParams } from '../../collection-rules';
@@ -35,6 +35,17 @@ export class CollectionStore extends Store {
     return this.rootYDoc.getMap('setting');
   }
 
+  watchCollectionDataReady() {
+    return this.workspaceService.workspace.engine.doc
+      .docState$(this.workspaceService.workspace.id)
+      .pipe(
+        map(docState => {
+          return docState.ready;
+        }),
+        distinctUntilChanged()
+      );
+  }
+
   watchCollectionMetas() {
     return yjsGetPath(this.workspaceSettingYMap, 'collections').pipe(
       switchMap(yjsObserveDeep),
diff --git a/packages/frontend/core/src/modules/collection/stores/pinned-collection.ts b/packages/frontend/core/src/modules/collection/stores/pinned-collection.ts
index 3e5afa44c541a..8663c91673cda 100644
--- a/packages/frontend/core/src/modules/collection/stores/pinned-collection.ts
+++ b/packages/frontend/core/src/modules/collection/stores/pinned-collection.ts
@@ -13,6 +13,10 @@ export class PinnedCollectionStore extends Store {
     super();
   }
 
+  watchPinnedCollectionDataReady() {
+    return this.workspaceDBService.db.pinnedCollections.isReady$;
+  }
+
   watchPinnedCollections(): Observable<PinnedCollectionRecord[]> {
     return this.workspaceDBService.db.pinnedCollections.find$();
   }
diff --git a/packages/frontend/core/src/modules/db/entities/table.ts b/packages/frontend/core/src/modules/db/entities/table.ts
index 37621a7cebfaa..60db9d2a30c4c 100644
--- a/packages/frontend/core/src/modules/db/entities/table.ts
+++ b/packages/frontend/core/src/modules/db/entities/table.ts
@@ -3,7 +3,7 @@ import type {
   TableSchemaBuilder,
 } from '@toeverything/infra';
 import { Entity, LiveData } from '@toeverything/infra';
-import { map } from 'rxjs';
+import { distinctUntilChanged, map } from 'rxjs';
 
 import type { WorkspaceService } from '../../workspace';
 
@@ -19,10 +19,23 @@ export class WorkspaceDBTable<
     super();
   }
 
+  isReady$ = LiveData.from(
+    this.workspaceService.workspace.engine.doc
+      .docState$(this.props.storageDocId)
+      .pipe(
+        map(docState => docState.ready),
+        distinctUntilChanged()
+      ),
+    false
+  );
+
   isSyncing$ = LiveData.from(
     this.workspaceService.workspace.engine.doc
       .docState$(this.props.storageDocId)
-      .pipe(map(docState => docState.syncing)),
+      .pipe(
+        map(docState => docState.syncing),
+        distinctUntilChanged()
+      ),
     false
   );
 


Reviews:
user: login: graphite-app[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T04:04:14Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 4**

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>packages/frontend/core/src/modules/db/entities/table.ts (1)</summary><blockquote>

`22-30`: **Minor: avoid re-subscribing to the same stream three times**

`isReady$`, `isSyncing$`, and `isLoading$` each create a fresh subscription to `docState$(storageDocId)`. A tiny optimisation is to memoise the base stream once:

```ts
const docState$ = this.workspaceService.workspace.engine.doc.docState$(
  this.props.storageDocId
).pipe(shareReplay(1)); // or LiveData.from(...)

isReady$   = LiveData.from(docState$.pipe(map(s => s.ready),   distinctUntilChanged()), false);
isSyncing$ = LiveData.from(docState$.pipe(map(s => s.syncing), distinctUntilChanged()), false);
isLoading$ = LiveData.from(docState$.pipe(map(s => !s.loaded)), false);
```

Not critical, but improves efficiency and readability.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 ESLint</summary>

[error] 22-22: Finnish notation should not be used here.

(rxjs/finnish)

</details>

</details>

</blockquote></details>
<details>
<summary>packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.css.ts (2)</summary><blockquote>

`30-46`: **Mask gradient needs WebKit prefix for cross-browser support**

`mask:` is still only fully supported behind `-webkit-mask` in Safari and some Chromium builds. Add the prefixed variant to avoid broken hover behaviour:

```diff
       mask:
         'linear-gradient(#fff) left / calc(100% - 32px) no-repeat,' +
         'linear-gradient(90deg,#fff 0%,transparent 50%,transparent 100%) right / 32px no-repeat',
+      WebkitMask:
+        'linear-gradient(#fff) left / calc(100% - 32px) no-repeat,' +
+        'linear-gradient(90deg,#fff 0%,transparent 50%,transparent 100%) right / 32px no-repeat',
```

(Safari Technology Preview 183 still requires the prefix.)

---

`48-58`: **Add a small transition for smoother icon-button reveal**

Instant opacity jumps can feel abrupt. A quick enhancement:

```diff
 export const editIconButton = style({
   opacity: 0,
   marginLeft: -16,
   backgroundColor: cssVarV2('layer/background/hoverOverlay'),
+  transition: 'opacity 120ms ease',
```

Improves perceived polish without extra cost.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 74b2d4dc2e3232d237d0e5bc895ce3ce372ff12d and 0817562acfc2b17350ea8dbf0a1ee3ecbd0515dc.

</details>

<details>
<summary>📒 Files selected for processing (8)</summary>

* `packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx` (5 hunks)
* `packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.css.ts` (1 hunks)
* `packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.tsx` (7 hunks)
* `packages/frontend/core/src/modules/collection/services/collection.ts` (1 hunks)
* `packages/frontend/core/src/modules/collection/services/pinned-collection.ts` (1 hunks)
* `packages/frontend/core/src/modules/collection/stores/collection.ts` (2 hunks)
* `packages/frontend/core/src/modules/collection/stores/pinned-collection.ts` (1 hunks)
* `packages/frontend/core/src/modules/db/entities/table.ts` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 ESLint</summary>

<details>
<summary>packages/frontend/core/src/modules/collection/services/collection.ts</summary>

[error] 22-22: Finnish notation should not be used here.

(rxjs/finnish)

</details>
<details>
<summary>packages/frontend/core/src/modules/db/entities/table.ts</summary>

[error] 22-22: Finnish notation should not be used here.

(rxjs/finnish)

---

[error] 32-32: Finnish notation should not be used here.

(rxjs/finnish)

</details>
<details>
<summary>packages/frontend/core/src/modules/collection/services/pinned-collection.ts</summary>

[error] 17-17: Finnish notation should not be used here.

(rxjs/finnish)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (773)</summary>

* GitHub Check: Server E2E Test
* GitHub Check: Cloud E2E Test 2/6
* GitHub Check: Server Test (7, 8)
* GitHub Check: Cloud E2E Test 4/6
* GitHub Check: Server Test (3, 8)
* GitHub Check: Server Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (3, 8)
* GitHub Check: Cloud E2E Test 3/6
* GitHub Check: Server Test (4, 8)
* GitHub Check: Server Test (6, 8)
* GitHub Check: Frontend Copilot E2E Test (4, 8)
* GitHub Check: Server Test (0, 8)
* GitHub Check: Frontend Copilot E2E Test (7, 8)
* GitHub Check: Frontend Copilot E2E Test (6, 8)
* GitHub Check: Cloud E2E Test 6/6
* GitHub Check: Frontend Copilot E2E Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (2, 8)
* GitHub Check: Cloud Desktop E2E Test
* GitHub Check: Frontend Copilot E2E Test (8, 8)
* GitHub Check: Cloud E2E Test 1/6
* GitHub Check: Frontend Copilot E2E Test (1, 8)
* GitHub Check: Cloud E2E Test 5/6
* GitHub Check: Server Test (1, 8)
* GitHub Check: Check Git Status
* GitHub Check: Server Test (2, 8)
* GitHub Check: Server Copilot Api Test
* GitHub Check: Unit Test (1)
* GitHub Check: Unit Test (5)
* GitHub Check: Unit Test (3)
* GitHub Check: Unit Test (4)
* GitHub Check: Unit Test (2)
* GitHub Check: test-build-mobile-app / build-ios-web
* GitHub Check: test-build-mobile-app / build-android-web
* GitHub Check: E2E Mobile Test (1)
* GitHub Check: E2E Mobile Test (3)
* GitHub Check: E2E Test (9)
* GitHub Check: E2E Test (8)
* GitHub Check: E2E Test (10)
* GitHub Check: y-octo binding test on x86_64-apple-darwin
* GitHub Check: E2E BlockSuite Cross Browser Test (1, webkit)
* GitHub Check: E2E Test (3)
* GitHub Check: E2E Test (5)
* GitHub Check: y-octo binding test on x86_64-pc-windows-msvc
* GitHub Check: E2E Test (1)
* GitHub Check: E2E Test (7)
* GitHub Check: E2E Test (2)
* GitHub Check: E2E Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, firefox)
* GitHub Check: Run native tests
* GitHub Check: E2E BlockSuite Test (9)
* GitHub Check: y-octo binding test on aarch64-pc-windows-msvc
* GitHub Check: E2E Test (6)
* GitHub Check: Build AFFiNE native (x86_64-pc-windows-msvc)
* GitHub Check: Build AFFiNE native (aarch64-pc-windows-msvc)
* GitHub Check: E2E BlockSuite Test (6)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, chromium)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, chromium)
* GitHub Check: E2E BlockSuite Test (8)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, firefox)
* GitHub Check: E2E BlockSuite Test (10)
* GitHub Check: E2E Mobile Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, webkit)
* GitHub Check: loom thread test
* GitHub Check: E2E BlockSuite Test (2)
* GitHub Check: E2E Mobile Test (5)
* GitHub Check: E2E BlockSuite Test (4)
* GitHub Check: E2E Mobile Test (2)
* GitHub Check: E2E BlockSuite Test (1)
* GitHub Check: E2E BlockSuite Test (7)
* GitHub Check: E2E BlockSuite Test (5)
* GitHub Check: E2E BlockSuite Test (3)
* GitHub Check: fuzzing
* GitHub Check: Typecheck
* GitHub Check: Analyze (javascript, blocksuite)
* GitHub Check: Build @affine/electron renderer
* GitHub Check: Analyze (typescript, blocksuite)
* GitHub Check: Analyze (javascript, affine)
* GitHub Check: Analyze (typescript, affine)
* GitHub Check: Server E2E Test
* GitHub Check: Cloud E2E Test 2/6
* GitHub Check: Server Test (7, 8)
* GitHub Check: Cloud E2E Test 4/6
* GitHub Check: Server Test (3, 8)
* GitHub Check: Server Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (3, 8)
* GitHub Check: Cloud E2E Test 3/6
* GitHub Check: Server Test (4, 8)
* GitHub Check: Server Test (6, 8)
* GitHub Check: Frontend Copilot E2E Test (4, 8)
* GitHub Check: Server Test (0, 8)
* GitHub Check: Frontend Copilot E2E Test (7, 8)
* GitHub Check: Frontend Copilot E2E Test (6, 8)
* GitHub Check: Cloud E2E Test 6/6
* GitHub Check: Frontend Copilot E2E Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (2, 8)
* GitHub Check: Cloud Desktop E2E Test
* GitHub Check: Frontend Copilot E2E Test (8, 8)
* GitHub Check: Cloud E2E Test 1/6
* GitHub Check: Frontend Copilot E2E Test (1, 8)
* GitHub Check: Cloud E2E Test 5/6
* GitHub Check: Server Test (1, 8)
* GitHub Check: Check Git Status
* GitHub Check: Server Test (2, 8)
* GitHub Check: Server Copilot Api Test
* GitHub Check: Unit Test (1)
* GitHub Check: Unit Test (5)
* GitHub Check: Unit Test (3)
* GitHub Check: Unit Test (4)
* GitHub Check: Unit Test (2)
* GitHub Check: test-build-mobile-app / build-ios-web
* GitHub Check: test-build-mobile-app / build-android-web
* GitHub Check: E2E Mobile Test (1)
* GitHub Check: E2E Mobile Test (3)
* GitHub Check: E2E Test (9)
* GitHub Check: E2E Test (8)
* GitHub Check: E2E Test (10)
* GitHub Check: y-octo binding test on x86_64-apple-darwin
* GitHub Check: E2E BlockSuite Cross Browser Test (1, webkit)
* GitHub Check: E2E Test (3)
* GitHub Check: E2E Test (5)
* GitHub Check: y-octo binding test on x86_64-pc-windows-msvc
* GitHub Check: E2E Test (1)
* GitHub Check: E2E Test (7)
* GitHub Check: E2E Test (2)
* GitHub Check: E2E Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, firefox)
* GitHub Check: Run native tests
* GitHub Check: E2E BlockSuite Test (9)
* GitHub Check: y-octo binding test on aarch64-pc-windows-msvc
* GitHub Check: E2E Test (6)
* GitHub Check: Build AFFiNE native (x86_64-pc-windows-msvc)
* GitHub Check: Build AFFiNE native (aarch64-pc-windows-msvc)
* GitHub Check: E2E BlockSuite Test (6)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, chromium)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, chromium)
* GitHub Check: E2E BlockSuite Test (8)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, firefox)
* GitHub Check: E2E BlockSuite Test (10)
* GitHub Check: E2E Mobile Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, webkit)
* GitHub Check: loom thread test
* GitHub Check: E2E BlockSuite Test (2)
* GitHub Check: E2E Mobile Test (5)
* GitHub Check: E2E BlockSuite Test (4)
* GitHub Check: E2E Mobile Test (2)
* GitHub Check: E2E BlockSuite Test (1)
* GitHub Check: E2E BlockSuite Test (7)
* GitHub Check: E2E BlockSuite Test (5)
* GitHub Check: E2E BlockSuite Test (3)
* GitHub Check: fuzzing
* GitHub Check: Typecheck
* GitHub Check: Analyze (javascript, blocksuite)
* GitHub Check: Build @affine/electron renderer
* GitHub Check: Analyze (typescript, blocksuite)
* GitHub Check: Analyze (javascript, affine)
* GitHub Check: Analyze (typescript, affine)
* GitHub Check: Server E2E Test
* GitHub Check: Cloud E2E Test 2/6
* GitHub Check: Server Test (7, 8)
* GitHub Check: Cloud E2E Test 4/6
* GitHub Check: Server Test (3, 8)
* GitHub Check: Server Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (3, 8)
* GitHub Check: Cloud E2E Test 3/6
* GitHub Check: Server Test (4, 8)
* GitHub Check: Server Test (6, 8)
* GitHub Check: Frontend Copilot E2E Test (4, 8)
* GitHub Check: Server Test (0, 8)
* GitHub Check: Frontend Copilot E2E Test (7, 8)
* GitHub Check: Frontend Copilot E2E Test (6, 8)
* GitHub Check: Cloud E2E Test 6/6
* GitHub Check: Frontend Copilot E2E Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (2, 8)
* GitHub Check: Cloud Desktop E2E Test
* GitHub Check: Frontend Copilot E2E Test (8, 8)
* GitHub Check: Cloud E2E Test 1/6
* GitHub Check: Frontend Copilot E2E Test (1, 8)
* GitHub Check: Cloud E2E Test 5/6
* GitHub Check: Server Test (1, 8)
* GitHub Check: Check Git Status
* GitHub Check: Server Test (2, 8)
* GitHub Check: Server Copilot Api Test
* GitHub Check: Unit Test (1)
* GitHub Check: Unit Test (5)
* GitHub Check: Unit Test (3)
* GitHub Check: Unit Test (4)
* GitHub Check: Unit Test (2)
* GitHub Check: test-build-mobile-app / build-ios-web
* GitHub Check: test-build-mobile-app / build-android-web
* GitHub Check: E2E Mobile Test (1)
* GitHub Check: E2E Mobile Test (3)
* GitHub Check: E2E Test (9)
* GitHub Check: E2E Test (8)
* GitHub Check: E2E Test (10)
* GitHub Check: y-octo binding test on x86_64-apple-darwin
* GitHub Check: E2E BlockSuite Cross Browser Test (1, webkit)
* GitHub Check: E2E Test (3)
* GitHub Check: E2E Test (5)
* GitHub Check: y-octo binding test on x86_64-pc-windows-msvc
* GitHub Check: E2E Test (1)
* GitHub Check: E2E Test (7)
* GitHub Check: E2E Test (2)
* GitHub Check: E2E Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, firefox)
* GitHub Check: Run native tests
* GitHub Check: E2E BlockSuite Test (9)
* GitHub Check: y-octo binding test on aarch64-pc-windows-msvc
* GitHub Check: E2E Test (6)
* GitHub Check: Build AFFiNE native (x86_64-pc-windows-msvc)
* GitHub Check: Build AFFiNE native (aarch64-pc-windows-msvc)
* GitHub Check: E2E BlockSuite Test (6)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, chromium)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, chromium)
* GitHub Check: E2E BlockSuite Test (8)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, firefox)
* GitHub Check: E2E BlockSuite Test (10)
* GitHub Check: E2E Mobile Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, webkit)
* GitHub Check: loom thread test
* GitHub Check: E2E BlockSuite Test (2)
* GitHub Check: E2E Mobile Test (5)
* GitHub Check: E2E BlockSuite Test (4)
* GitHub Check: E2E Mobile Test (2)
* GitHub Check: E2E BlockSuite Test (1)
* GitHub Check: E2E BlockSuite Test (7)
* GitHub Check: E2E BlockSuite Test (5)
* GitHub Check: E2E BlockSuite Test (3)
* GitHub Check: fuzzing
* GitHub Check: Typecheck
* GitHub Check: Analyze (javascript, blocksuite)
* GitHub Check: Build @affine/electron renderer
* GitHub Check: Analyze (typescript, blocksuite)
* GitHub Check: Analyze (javascript, affine)
* GitHub Check: Analyze (typescript, affine)
* GitHub Check: Server E2E Test
* GitHub Check: Cloud E2E Test 2/6
* GitHub Check: Server Test (7, 8)
* GitHub Check: Cloud E2E Test 4/6
* GitHub Check: Server Test (3, 8)
* GitHub Check: Server Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (3, 8)
* GitHub Check: Cloud E2E Test 3/6
* GitHub Check: Server Test (4, 8)
* GitHub Check: Server Test (6, 8)
* GitHub Check: Frontend Copilot E2E Test (4, 8)
* GitHub Check: Server Test (0, 8)
* GitHub Check: Frontend Copilot E2E Test (7, 8)
* GitHub Check: Frontend Copilot E2E Test (6, 8)
* GitHub Check: Cloud E2E Test 6/6
* GitHub Check: Frontend Copilot E2E Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (2, 8)
* GitHub Check: Cloud Desktop E2E Test
* GitHub Check: Frontend Copilot E2E Test (8, 8)
* GitHub Check: Cloud E2E Test 1/6
* GitHub Check: Frontend Copilot E2E Test (1, 8)
* GitHub Check: Cloud E2E Test 5/6
* GitHub Check: Server Test (1, 8)
* GitHub Check: Check Git Status
* GitHub Check: Server Test (2, 8)
* GitHub Check: Server Copilot Api Test
* GitHub Check: Unit Test (1)
* GitHub Check: Unit Test (5)
* GitHub Check: Unit Test (3)
* GitHub Check: Unit Test (4)
* GitHub Check: Unit Test (2)
* GitHub Check: test-build-mobile-app / build-ios-web
* GitHub Check: test-build-mobile-app / build-android-web
* GitHub Check: E2E Mobile Test (1)
* GitHub Check: E2E Mobile Test (3)
* GitHub Check: E2E Test (9)
* GitHub Check: E2E Test (8)
* GitHub Check: E2E Test (10)
* GitHub Check: y-octo binding test on x86_64-apple-darwin
* GitHub Check: E2E BlockSuite Cross Browser Test (1, webkit)
* GitHub Check: E2E Test (3)
* GitHub Check: E2E Test (5)
* GitHub Check: y-octo binding test on x86_64-pc-windows-msvc
* GitHub Check: E2E Test (1)
* GitHub Check: E2E Test (7)
* GitHub Check: E2E Test (2)
* GitHub Check: E2E Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, firefox)
* GitHub Check: Run native tests
* GitHub Check: E2E BlockSuite Test (9)
* GitHub Check: y-octo binding test on aarch64-pc-windows-msvc
* GitHub Check: E2E Test (6)
* GitHub Check: Build AFFiNE native (x86_64-pc-windows-msvc)
* GitHub Check: Build AFFiNE native (aarch64-pc-windows-msvc)
* GitHub Check: E2E BlockSuite Test (6)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, chromium)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, chromium)
* GitHub Check: E2E BlockSuite Test (8)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, firefox)
* GitHub Check: E2E BlockSuite Test (10)
* GitHub Check: E2E Mobile Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, webkit)
* GitHub Check: E2E BlockSuite Test (2)
* GitHub Check: E2E Mobile Test (5)
* GitHub Check: E2E BlockSuite Test (4)
* GitHub Check: E2E Mobile Test (2)
* GitHub Check: E2E BlockSuite Test (1)
* GitHub Check: E2E BlockSuite Test (7)
* GitHub Check: E2E BlockSuite Test (5)
* GitHub Check: E2E BlockSuite Test (3)
* GitHub Check: fuzzing
* GitHub Check: Typecheck
* GitHub Check: Analyze (javascript, blocksuite)
* GitHub Check: Build @affine/electron renderer
* GitHub Check: Analyze (typescript, blocksuite)
* GitHub Check: Analyze (javascript, affine)
* GitHub Check: Analyze (typescript, affine)
* GitHub Check: Server E2E Test
* GitHub Check: Cloud E2E Test 2/6
* GitHub Check: Server Test (7, 8)
* GitHub Check: Cloud E2E Test 4/6
* GitHub Check: Server Test (3, 8)
* GitHub Check: Server Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (3, 8)
* GitHub Check: Cloud E2E Test 3/6
* GitHub Check: Server Test (4, 8)
* GitHub Check: Server Test (6, 8)
* GitHub Check: Frontend Copilot E2E Test (4, 8)
* GitHub Check: Server Test (0, 8)
* GitHub Check: Frontend Copilot E2E Test (7, 8)
* GitHub Check: Frontend Copilot E2E Test (6, 8)
* GitHub Check: Cloud E2E Test 6/6
* GitHub Check: Frontend Copilot E2E Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (2, 8)
* GitHub Check: Cloud Desktop E2E Test
* GitHub Check: Frontend Copilot E2E Test (8, 8)
* GitHub Check: Cloud E2E Test 1/6
* GitHub Check: Frontend Copilot E2E Test (1, 8)
* GitHub Check: Cloud E2E Test 5/6
* GitHub Check: Server Test (1, 8)
* GitHub Check: Check Git Status
* GitHub Check: Server Test (2, 8)
* GitHub Check: Server Copilot Api Test
* GitHub Check: Unit Test (1)
* GitHub Check: Unit Test (5)
* GitHub Check: Unit Test (3)
* GitHub Check: Unit Test (4)
* GitHub Check: Unit Test (2)
* GitHub Check: test-build-mobile-app / build-ios-web
* GitHub Check: test-build-mobile-app / build-android-web
* GitHub Check: E2E Mobile Test (1)
* GitHub Check: E2E Mobile Test (3)
* GitHub Check: E2E Test (9)
* GitHub Check: E2E Test (8)
* GitHub Check: E2E Test (10)
* GitHub Check: y-octo binding test on x86_64-apple-darwin
* GitHub Check: E2E BlockSuite Cross Browser Test (1, webkit)
* GitHub Check: E2E Test (3)
* GitHub Check: E2E Test (5)
* GitHub Check: y-octo binding test on x86_64-pc-windows-msvc
* GitHub Check: E2E Test (1)
* GitHub Check: E2E Test (7)
* GitHub Check: E2E Test (2)
* GitHub Check: E2E Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, firefox)
* GitHub Check: Run native tests
* GitHub Check: E2E BlockSuite Test (9)
* GitHub Check: y-octo binding test on aarch64-pc-windows-msvc
* GitHub Check: E2E Test (6)
* GitHub Check: Build AFFiNE native (x86_64-pc-windows-msvc)
* GitHub Check: Build AFFiNE native (aarch64-pc-windows-msvc)
* GitHub Check: E2E BlockSuite Test (6)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, chromium)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, chromium)
* GitHub Check: E2E BlockSuite Test (8)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, firefox)
* GitHub Check: E2E BlockSuite Test (10)
* GitHub Check: E2E Mobile Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, webkit)
* GitHub Check: E2E BlockSuite Test (2)
* GitHub Check: E2E Mobile Test (5)
* GitHub Check: E2E BlockSuite Test (4)
* GitHub Check: E2E Mobile Test (2)
* GitHub Check: E2E BlockSuite Test (1)
* GitHub Check: E2E BlockSuite Test (7)
* GitHub Check: E2E BlockSuite Test (5)
* GitHub Check: E2E BlockSuite Test (3)
* GitHub Check: fuzzing
* GitHub Check: Typecheck
* GitHub Check: Analyze (javascript, blocksuite)
* GitHub Check: Build @affine/electron renderer
* GitHub Check: Analyze (typescript, blocksuite)
* GitHub Check: Analyze (javascript, affine)
* GitHub Check: Analyze (typescript, affine)
* GitHub Check: Server E2E Test
* GitHub Check: Cloud E2E Test 2/6
* GitHub Check: Server Test (7, 8)
* GitHub Check: Cloud E2E Test 4/6
* GitHub Check: Server Test (3, 8)
* GitHub Check: Server Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (3, 8)
* GitHub Check: Cloud E2E Test 3/6
* GitHub Check: Server Test (4, 8)
* GitHub Check: Server Test (6, 8)
* GitHub Check: Frontend Copilot E2E Test (4, 8)
* GitHub Check: Server Test (0, 8)
* GitHub Check: Frontend Copilot E2E Test (7, 8)
* GitHub Check: Frontend Copilot E2E Test (6, 8)
* GitHub Check: Cloud E2E Test 6/6
* GitHub Check: Frontend Copilot E2E Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (2, 8)
* GitHub Check: Cloud Desktop E2E Test
* GitHub Check: Frontend Copilot E2E Test (8, 8)
* GitHub Check: Cloud E2E Test 1/6
* GitHub Check: Frontend Copilot E2E Test (1, 8)
* GitHub Check: Cloud E2E Test 5/6
* GitHub Check: Server Test (1, 8)
* GitHub Check: Check Git Status
* GitHub Check: Server Test (2, 8)
* GitHub Check: Server Copilot Api Test
* GitHub Check: Unit Test (1)
* GitHub Check: Unit Test (5)
* GitHub Check: Unit Test (3)
* GitHub Check: Unit Test (4)
* GitHub Check: Unit Test (2)
* GitHub Check: test-build-mobile-app / build-ios-web
* GitHub Check: test-build-mobile-app / build-android-web
* GitHub Check: E2E Mobile Test (1)
* GitHub Check: E2E Mobile Test (3)
* GitHub Check: E2E Test (9)
* GitHub Check: E2E Test (8)
* GitHub Check: E2E Test (10)
* GitHub Check: y-octo binding test on x86_64-apple-darwin
* GitHub Check: E2E BlockSuite Cross Browser Test (1, webkit)
* GitHub Check: E2E Test (3)
* GitHub Check: E2E Test (5)
* GitHub Check: y-octo binding test on x86_64-pc-windows-msvc
* GitHub Check: E2E Test (1)
* GitHub Check: E2E Test (7)
* GitHub Check: E2E Test (2)
* GitHub Check: E2E Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, firefox)
* GitHub Check: Run native tests
* GitHub Check: E2E BlockSuite Test (9)
* GitHub Check: y-octo binding test on aarch64-pc-windows-msvc
* GitHub Check: E2E Test (6)
* GitHub Check: Build AFFiNE native (x86_64-pc-windows-msvc)
* GitHub Check: Build AFFiNE native (aarch64-pc-windows-msvc)
* GitHub Check: E2E BlockSuite Test (6)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, chromium)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, chromium)
* GitHub Check: E2E BlockSuite Test (8)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, firefox)
* GitHub Check: E2E BlockSuite Test (10)
* GitHub Check: E2E Mobile Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, webkit)
* GitHub Check: E2E BlockSuite Test (2)
* GitHub Check: E2E Mobile Test (5)
* GitHub Check: E2E BlockSuite Test (4)
* GitHub Check: E2E Mobile Test (2)
* GitHub Check: E2E BlockSuite Test (1)
* GitHub Check: E2E BlockSuite Test (7)
* GitHub Check: E2E BlockSuite Test (5)
* GitHub Check: E2E BlockSuite Test (3)
* GitHub Check: fuzzing
* GitHub Check: Typecheck
* GitHub Check: Analyze (javascript, blocksuite)
* GitHub Check: Build @affine/electron renderer
* GitHub Check: Analyze (typescript, blocksuite)
* GitHub Check: Analyze (javascript, affine)
* GitHub Check: Analyze (typescript, affine)
* GitHub Check: Server E2E Test
* GitHub Check: Cloud E2E Test 2/6
* GitHub Check: Server Test (7, 8)
* GitHub Check: Cloud E2E Test 4/6
* GitHub Check: Server Test (3, 8)
* GitHub Check: Server Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (3, 8)
* GitHub Check: Cloud E2E Test 3/6
* GitHub Check: Server Test (4, 8)
* GitHub Check: Server Test (6, 8)
* GitHub Check: Frontend Copilot E2E Test (4, 8)
* GitHub Check: Server Test (0, 8)
* GitHub Check: Frontend Copilot E2E Test (7, 8)
* GitHub Check: Frontend Copilot E2E Test (6, 8)
* GitHub Check: Cloud E2E Test 6/6
* GitHub Check: Frontend Copilot E2E Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (2, 8)
* GitHub Check: Cloud Desktop E2E Test
* GitHub Check: Frontend Copilot E2E Test (8, 8)
* GitHub Check: Cloud E2E Test 1/6
* GitHub Check: Frontend Copilot E2E Test (1, 8)
* GitHub Check: Cloud E2E Test 5/6
* GitHub Check: Server Test (1, 8)
* GitHub Check: Check Git Status
* GitHub Check: Server Test (2, 8)
* GitHub Check: Server Copilot Api Test
* GitHub Check: Unit Test (1)
* GitHub Check: Unit Test (5)
* GitHub Check: Unit Test (3)
* GitHub Check: Unit Test (4)
* GitHub Check: Unit Test (2)
* GitHub Check: test-build-mobile-app / build-ios-web
* GitHub Check: test-build-mobile-app / build-android-web
* GitHub Check: E2E Mobile Test (1)
* GitHub Check: E2E Mobile Test (3)
* GitHub Check: E2E Test (9)
* GitHub Check: E2E Test (8)
* GitHub Check: E2E Test (10)
* GitHub Check: y-octo binding test on x86_64-apple-darwin
* GitHub Check: E2E BlockSuite Cross Browser Test (1, webkit)
* GitHub Check: E2E Test (3)
* GitHub Check: E2E Test (5)
* GitHub Check: y-octo binding test on x86_64-pc-windows-msvc
* GitHub Check: E2E Test (1)
* GitHub Check: E2E Test (7)
* GitHub Check: E2E Test (2)
* GitHub Check: E2E Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, firefox)
* GitHub Check: Run native tests
* GitHub Check: E2E BlockSuite Test (9)
* GitHub Check: y-octo binding test on aarch64-pc-windows-msvc
* GitHub Check: E2E Test (6)
* GitHub Check: Build AFFiNE native (x86_64-pc-windows-msvc)
* GitHub Check: Build AFFiNE native (aarch64-pc-windows-msvc)
* GitHub Check: E2E BlockSuite Test (6)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, chromium)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, chromium)
* GitHub Check: E2E BlockSuite Test (8)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, firefox)
* GitHub Check: E2E BlockSuite Test (10)
* GitHub Check: E2E Mobile Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, webkit)
* GitHub Check: E2E BlockSuite Test (2)
* GitHub Check: E2E Mobile Test (5)
* GitHub Check: E2E BlockSuite Test (4)
* GitHub Check: E2E Mobile Test (2)
* GitHub Check: E2E BlockSuite Test (1)
* GitHub Check: E2E BlockSuite Test (7)
* GitHub Check: E2E BlockSuite Test (5)
* GitHub Check: E2E BlockSuite Test (3)
* GitHub Check: fuzzing
* GitHub Check: Typecheck
* GitHub Check: Analyze (javascript, blocksuite)
* GitHub Check: Build @affine/electron renderer
* GitHub Check: Analyze (typescript, blocksuite)
* GitHub Check: Analyze (javascript, affine)
* GitHub Check: Analyze (typescript, affine)
* GitHub Check: Server E2E Test
* GitHub Check: Cloud E2E Test 2/6
* GitHub Check: Server Test (7, 8)
* GitHub Check: Cloud E2E Test 4/6
* GitHub Check: Server Test (3, 8)
* GitHub Check: Server Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (3, 8)
* GitHub Check: Cloud E2E Test 3/6
* GitHub Check: Server Test (4, 8)
* GitHub Check: Server Test (6, 8)
* GitHub Check: Frontend Copilot E2E Test (4, 8)
* GitHub Check: Server Test (0, 8)
* GitHub Check: Frontend Copilot E2E Test (7, 8)
* GitHub Check: Frontend Copilot E2E Test (6, 8)
* GitHub Check: Cloud E2E Test 6/6
* GitHub Check: Frontend Copilot E2E Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (2, 8)
* GitHub Check: Cloud Desktop E2E Test
* GitHub Check: Frontend Copilot E2E Test (8, 8)
* GitHub Check: Cloud E2E Test 1/6
* GitHub Check: Frontend Copilot E2E Test (1, 8)
* GitHub Check: Cloud E2E Test 5/6
* GitHub Check: Server Test (1, 8)
* GitHub Check: Check Git Status
* GitHub Check: Server Test (2, 8)
* GitHub Check: Server Copilot Api Test
* GitHub Check: Unit Test (1)
* GitHub Check: Unit Test (5)
* GitHub Check: Unit Test (3)
* GitHub Check: Unit Test (4)
* GitHub Check: Unit Test (2)
* GitHub Check: test-build-mobile-app / build-ios-web
* GitHub Check: test-build-mobile-app / build-android-web
* GitHub Check: E2E Mobile Test (1)
* GitHub Check: E2E Mobile Test (3)
* GitHub Check: E2E Test (9)
* GitHub Check: E2E Test (8)
* GitHub Check: E2E Test (10)
* GitHub Check: y-octo binding test on x86_64-apple-darwin
* GitHub Check: E2E BlockSuite Cross Browser Test (1, webkit)
* GitHub Check: E2E Test (3)
* GitHub Check: E2E Test (5)
* GitHub Check: y-octo binding test on x86_64-pc-windows-msvc
* GitHub Check: E2E Test (1)
* GitHub Check: E2E Test (7)
* GitHub Check: E2E Test (2)
* GitHub Check: E2E Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, firefox)
* GitHub Check: Run native tests
* GitHub Check: E2E BlockSuite Test (9)
* GitHub Check: y-octo binding test on aarch64-pc-windows-msvc
* GitHub Check: E2E Test (6)
* GitHub Check: Build AFFiNE native (x86_64-pc-windows-msvc)
* GitHub Check: Build AFFiNE native (aarch64-pc-windows-msvc)
* GitHub Check: E2E BlockSuite Test (6)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, chromium)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, chromium)
* GitHub Check: E2E BlockSuite Test (8)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, firefox)
* GitHub Check: E2E BlockSuite Test (10)
* GitHub Check: E2E Mobile Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, webkit)
* GitHub Check: E2E BlockSuite Test (2)
* GitHub Check: E2E Mobile Test (5)
* GitHub Check: E2E BlockSuite Test (4)
* GitHub Check: E2E Mobile Test (2)
* GitHub Check: E2E BlockSuite Test (1)
* GitHub Check: E2E BlockSuite Test (7)
* GitHub Check: E2E BlockSuite Test (5)
* GitHub Check: E2E BlockSuite Test (3)
* GitHub Check: fuzzing
* GitHub Check: Typecheck
* GitHub Check: Analyze (javascript, blocksuite)
* GitHub Check: Build @affine/electron renderer
* GitHub Check: Analyze (typescript, blocksuite)
* GitHub Check: Analyze (javascript, affine)
* GitHub Check: Analyze (typescript, affine)
* GitHub Check: Server E2E Test
* GitHub Check: Cloud E2E Test 2/6
* GitHub Check: Server Test (7, 8)
* GitHub Check: Cloud E2E Test 4/6
* GitHub Check: Server Test (3, 8)
* GitHub Check: Server Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (3, 8)
* GitHub Check: Cloud E2E Test 3/6
* GitHub Check: Server Test (4, 8)
* GitHub Check: Server Test (6, 8)
* GitHub Check: Frontend Copilot E2E Test (4, 8)
* GitHub Check: Server Test (0, 8)
* GitHub Check: Frontend Copilot E2E Test (7, 8)
* GitHub Check: Frontend Copilot E2E Test (6, 8)
* GitHub Check: Cloud E2E Test 6/6
* GitHub Check: Frontend Copilot E2E Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (2, 8)
* GitHub Check: Cloud Desktop E2E Test
* GitHub Check: Frontend Copilot E2E Test (8, 8)
* GitHub Check: Cloud E2E Test 1/6
* GitHub Check: Frontend Copilot E2E Test (1, 8)
* GitHub Check: Cloud E2E Test 5/6
* GitHub Check: Server Test (1, 8)
* GitHub Check: Check Git Status
* GitHub Check: Server Test (2, 8)
* GitHub Check: Server Copilot Api Test
* GitHub Check: Unit Test (1)
* GitHub Check: Unit Test (5)
* GitHub Check: Unit Test (3)
* GitHub Check: Unit Test (4)
* GitHub Check: Unit Test (2)
* GitHub Check: test-build-mobile-app / build-ios-web
* GitHub Check: test-build-mobile-app / build-android-web
* GitHub Check: E2E Mobile Test (1)
* GitHub Check: E2E Mobile Test (3)
* GitHub Check: E2E Test (9)
* GitHub Check: E2E Test (8)
* GitHub Check: E2E Test (10)
* GitHub Check: y-octo binding test on x86_64-apple-darwin
* GitHub Check: E2E BlockSuite Cross Browser Test (1, webkit)
* GitHub Check: E2E Test (3)
* GitHub Check: E2E Test (5)
* GitHub Check: y-octo binding test on x86_64-pc-windows-msvc
* GitHub Check: E2E Test (1)
* GitHub Check: E2E Test (7)
* GitHub Check: E2E Test (2)
* GitHub Check: E2E Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, firefox)
* GitHub Check: Run native tests
* GitHub Check: E2E BlockSuite Test (9)
* GitHub Check: y-octo binding test on aarch64-pc-windows-msvc
* GitHub Check: E2E Test (6)
* GitHub Check: Build AFFiNE native (x86_64-pc-windows-msvc)
* GitHub Check: Build AFFiNE native (aarch64-pc-windows-msvc)
* GitHub Check: E2E BlockSuite Test (6)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, chromium)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, chromium)
* GitHub Check: E2E BlockSuite Test (8)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, firefox)
* GitHub Check: E2E BlockSuite Test (10)
* GitHub Check: E2E Mobile Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, webkit)
* GitHub Check: E2E BlockSuite Test (2)
* GitHub Check: E2E Mobile Test (5)
* GitHub Check: E2E BlockSuite Test (4)
* GitHub Check: E2E Mobile Test (2)
* GitHub Check: E2E BlockSuite Test (1)
* GitHub Check: E2E BlockSuite Test (7)
* GitHub Check: E2E BlockSuite Test (5)
* GitHub Check: E2E BlockSuite Test (3)
* GitHub Check: fuzzing
* GitHub Check: Typecheck
* GitHub Check: Analyze (javascript, blocksuite)
* GitHub Check: Build @affine/electron renderer
* GitHub Check: Analyze (typescript, blocksuite)
* GitHub Check: Analyze (javascript, affine)
* GitHub Check: Analyze (typescript, affine)
* GitHub Check: Server E2E Test
* GitHub Check: Cloud E2E Test 2/6
* GitHub Check: Server Test (7, 8)
* GitHub Check: Cloud E2E Test 4/6
* GitHub Check: Server Test (3, 8)
* GitHub Check: Server Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (3, 8)
* GitHub Check: Cloud E2E Test 3/6
* GitHub Check: Server Test (4, 8)
* GitHub Check: Server Test (6, 8)
* GitHub Check: Frontend Copilot E2E Test (4, 8)
* GitHub Check: Server Test (0, 8)
* GitHub Check: Frontend Copilot E2E Test (7, 8)
* GitHub Check: Frontend Copilot E2E Test (6, 8)
* GitHub Check: Cloud E2E Test 6/6
* GitHub Check: Frontend Copilot E2E Test (5, 8)
* GitHub Check: Frontend Copilot E2E Test (2, 8)
* GitHub Check: Cloud Desktop E2E Test
* GitHub Check: Frontend Copilot E2E Test (8, 8)
* GitHub Check: Cloud E2E Test 1/6
* GitHub Check: Frontend Copilot E2E Test (1, 8)
* GitHub Check: Cloud E2E Test 5/6
* GitHub Check: Server Test (1, 8)
* GitHub Check: Check Git Status
* GitHub Check: Server Test (2, 8)
* GitHub Check: Server Copilot Api Test
* GitHub Check: Unit Test (1)
* GitHub Check: Unit Test (5)
* GitHub Check: Unit Test (3)
* GitHub Check: Unit Test (4)
* GitHub Check: Unit Test (2)
* GitHub Check: test-build-mobile-app / build-ios-web
* GitHub Check: test-build-mobile-app / build-android-web
* GitHub Check: E2E Mobile Test (1)
* GitHub Check: E2E Mobile Test (3)
* GitHub Check: E2E Test (9)
* GitHub Check: E2E Test (8)
* GitHub Check: E2E Test (10)
* GitHub Check: y-octo binding test on x86_64-apple-darwin
* GitHub Check: E2E BlockSuite Cross Browser Test (1, webkit)
* GitHub Check: E2E Test (3)
* GitHub Check: E2E Test (5)
* GitHub Check: y-octo binding test on x86_64-pc-windows-msvc
* GitHub Check: E2E Test (1)
* GitHub Check: E2E Test (7)
* GitHub Check: E2E Test (2)
* GitHub Check: E2E Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, firefox)
* GitHub Check: Run native tests
* GitHub Check: E2E BlockSuite Test (9)
* GitHub Check: y-octo binding test on aarch64-pc-windows-msvc
* GitHub Check: E2E Test (6)
* GitHub Check: Build AFFiNE native (x86_64-pc-windows-msvc)
* GitHub Check: Build AFFiNE native (aarch64-pc-windows-msvc)
* GitHub Check: E2E BlockSuite Test (6)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, chromium)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, chromium)
* GitHub Check: E2E BlockSuite Test (8)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, firefox)
* GitHub Check: E2E BlockSuite Test (10)
* GitHub Check: E2E Mobile Test (4)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, webkit)
* GitHub Check: E2E BlockSuite Test (2)
* GitHub Check: E2E Mobile Test (5)
* GitHub Check: E2E BlockSuite Test (4)
* GitHub Check: E2E Mobile Test (2)
* GitHub Check: E2E BlockSuite Test (1)
* GitHub Check: E2E BlockSuite Test (7)
* GitHub Check: E2E BlockSuite Test (5)
* GitHub Check: E2E BlockSuite Test (3)
* GitHub Check: fuzzing
* GitHub Check: Typecheck
* GitHub Check: Analyze (javascript, blocksuite)
* GitHub Check: Build @affine/electron renderer
* GitHub Check: Analyze (typescript, blocksuite)
* GitHub Check: Analyze (javascript, affine)
* GitHub Check: Analyze (typescript, affine)

</details>

<details>
<summary>🔇 Additional comments (13)</summary><blockquote>

<details>
<summary>packages/frontend/core/src/modules/collection/stores/pinned-collection.ts (1)</summary>

`16-18`: **Looks good - adds reactive readiness tracking for pinned collections.**

This new method establishes a clear way to observe when pinned collections data is ready, following the same pattern as other observables in the codebase. This will help with properly synchronizing UI state with the data availability.

</details>
<details>
<summary>packages/frontend/core/src/modules/collection/services/pinned-collection.ts (1)</summary>

`17-20`: <details>
<summary>✅ Verification successful</summary>

**Exposes pinned collection readiness state correctly.**

This implementation properly wraps the store's readiness observable using LiveData with an appropriate initial value of `false`. This aligns well with the pattern used for other observables in this service.


While the implementation looks correct, there's an ESLint error about Finnish notation ($ suffix). This appears to be a consistent pattern in the codebase, but you might want to verify team preferences:

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if Finnish notation is used consistently in other LiveData observables
echo "Checking for Finnish notation pattern in LiveData observables:"
rg "LiveData\.from.*\$\s*=" packages/frontend/core/src/modules/
```

Length of output: 199

---

🏁 Script executed:

```shell
#!/bin/bash
# List all usages of LiveData.from with surrounding context to check variable naming
echo "Listing all LiveData.from usages with assignment context:"
rg -n "LiveData\\.from" -A3 packages/frontend/core/src/modules
```

Length of output: 39567

---



</details>

**PinnedCollectionDataReady$ is consistent with codebase conventions**  
The `pinnedCollectionDataReady$` observable correctly wraps the store’s readiness stream with `LiveData.from(…, false)` and follows the same `$` suffix naming used throughout the frontend core. No changes required.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 ESLint</summary>

[error] 17-17: Finnish notation should not be used here.

(rxjs/finnish)

</details>

</details>

</details>
<details>
<summary>packages/frontend/core/src/modules/collection/stores/collection.ts (2)</summary>

`10-10`: **Import addition looks good.**

Added `distinctUntilChanged` import from rxjs to support the new method implementation.

---

`38-47`: **Good implementation of collection data readiness observable.**

This method correctly exposes the document's ready state and uses `distinctUntilChanged()` to prevent unnecessary emissions when the ready state hasn't changed. This provides an efficient way for UI components to react only to actual changes in readiness.

</details>
<details>
<summary>packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.tsx (9)</summary>

`10-16`: **Added necessary icon imports for edit and delete UI elements**

The code now imports the required icons (CloseIcon, EditIcon) to support the new edit and delete functionality for pinned collections.

---

`22-34`: **Added callback props for edit and remove operations**

The PinnedCollectionItem component now accepts `onClickRemove` and `onClickEdit` callback props, enabling the edit and delete functionality requested in the PR. The type definitions are properly specified.

---

`51-73`: **Well-implemented UI controls for edit and delete**

The implementation of edit and delete buttons follows good practices:
1. Both buttons use IconButton for consistent UI
2. Event handlers use `stopPropagation()` to prevent triggering the parent onClick event
3. The close button is conditionally rendered only for active items, improving UX

The component structure follows React best practices with appropriate styling classes.

---

`78-91`: **Improved prop naming and added edit collection callback**

The component now:
1. Uses clearer prop names (`onActiveAll`, `onActiveCollection`) that better reflect their purpose
2. Adds a new `onEditCollection` callback prop to support edit functionality

These changes improve code readability and maintainability.

---

`106-115`: **Updated "All" item handler to use renamed prop**

The onClick handler for the "All" collection item now correctly uses the renamed `onActiveAll` prop. This maintains consistency with the prop naming changes.

---

`116-133`: **Well-implemented collection removal with fallback logic**

The removal handler implementation shows good attention to UX:
1. When removing a collection, it automatically selects the previous collection
2. If no previous collection exists, it falls back to the "All" collection
3. Only after setting the new active collection does it remove the pinned collection

This prevents UI jumps and ensures the user always has a selected collection.

---

`144-149`: **Renamed prop for consistency**

The `onAddPinnedCollection` prop was renamed to `onPinCollection` in the AddPinnedCollection component. This improves naming consistency throughout the codebase.

---

`167-172`: **Prop renaming continued for consistency**

Similar to the AddPinnedCollection component, the prop was renamed from `onAddPinnedCollection` to `onPinCollection` in the AddPinnedCollectionMenuContent component. This maintains the consistent naming convention.

---

`214-214`: **Updated callback to use renamed prop**

The MenuItem onClick handler now correctly calls the renamed `onPinCollection` prop. This completes the consistent prop renaming throughout the file.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T04:07:49Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 0817562acfc2b17350ea8dbf0a1ee3ecbd0515dc and afa108d5170c33439d4e653b3c5f9bdf46b4721a.

</details>

<details>
<summary>📒 Files selected for processing (8)</summary>

* `packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx` (5 hunks)
* `packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.css.ts` (1 hunks)
* `packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.tsx` (7 hunks)
* `packages/frontend/core/src/modules/collection/services/collection.ts` (1 hunks)
* `packages/frontend/core/src/modules/collection/services/pinned-collection.ts` (1 hunks)
* `packages/frontend/core/src/modules/collection/stores/collection.ts` (2 hunks)
* `packages/frontend/core/src/modules/collection/stores/pinned-collection.ts` (1 hunks)
* `packages/frontend/core/src/modules/db/entities/table.ts` (2 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (7)</summary>

* packages/frontend/core/src/modules/collection/stores/collection.ts
* packages/frontend/core/src/modules/collection/stores/pinned-collection.ts
* packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.css.ts
* packages/frontend/core/src/modules/collection/services/collection.ts
* packages/frontend/core/src/modules/db/entities/table.ts
* packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx
* packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.tsx

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 ESLint</summary>

<details>
<summary>packages/frontend/core/src/modules/collection/services/pinned-collection.ts</summary>

[error] 17-17: Finnish notation should not be used here.

(rxjs/finnish)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (69)</summary>

* GitHub Check: Cloud E2E Test 6/6
* GitHub Check: Cloud E2E Test 4/6
* GitHub Check: Cloud E2E Test 5/6
* GitHub Check: Cloud E2E Test 3/6
* GitHub Check: Cloud Desktop E2E Test
* GitHub Check: Cloud E2E Test 2/6
* GitHub Check: Cloud E2E Test 1/6
* GitHub Check: Server Test (6, 8)
* GitHub Check: Server Test (4, 8)
* GitHub Check: Server Test (2, 8)
* GitHub Check: Server Test (7, 8)
* GitHub Check: Server Test (5, 8)
* GitHub Check: Server Test (3, 8)
* GitHub Check: Server Test (0, 8)
* GitHub Check: Server Test (1, 8)
* GitHub Check: Server Copilot Api Test
* GitHub Check: Check Git Status
* GitHub Check: Server E2E Test
* GitHub Check: Unit Test (3)
* GitHub Check: Unit Test (5)
* GitHub Check: Unit Test (4)
* GitHub Check: Unit Test (2)
* GitHub Check: Unit Test (1)
* GitHub Check: test-build-mobile-app / build-ios-web
* GitHub Check: test-build-mobile-app / build-android-web
* GitHub Check: y-octo binding test on x86_64-pc-windows-msvc
* GitHub Check: y-octo binding test on aarch64-pc-windows-msvc
* GitHub Check: Run native tests
* GitHub Check: E2E BlockSuite Cross Browser Test (2, firefox)
* GitHub Check: fuzzing
* GitHub Check: Build @affine/electron renderer
* GitHub Check: Typecheck
* GitHub Check: E2E BlockSuite Cross Browser Test (1, webkit)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, chromium)
* GitHub Check: E2E BlockSuite Cross Browser Test (2, webkit)
* GitHub Check: E2E Test (10)
* GitHub Check: E2E BlockSuite Cross Browser Test (1, chromium)
* GitHub Check: loom thread test
* GitHub Check: E2E BlockSuite Cross Browser Test (1, firefox)
* GitHub Check: E2E Test (8)
* GitHub Check: E2E Test (6)
* GitHub Check: E2E Test (9)
* GitHub Check: E2E Test (5)
* GitHub Check: E2E Test (2)
* GitHub Check: E2E Test (7)
* GitHub Check: E2E Test (3)
* GitHub Check: E2E Test (1)
* GitHub Check: E2E Test (4)
* GitHub Check: E2E BlockSuite Test (8)
* GitHub Check: E2E BlockSuite Test (7)
* GitHub Check: E2E BlockSuite Test (10)
* GitHub Check: E2E BlockSuite Test (9)
* GitHub Check: E2E BlockSuite Test (6)
* GitHub Check: E2E BlockSuite Test (5)
* GitHub Check: E2E BlockSuite Test (1)
* GitHub Check: E2E BlockSuite Test (3)
* GitHub Check: E2E BlockSuite Test (2)
* GitHub Check: E2E BlockSuite Test (4)
* GitHub Check: E2E Mobile Test (5)
* GitHub Check: E2E Mobile Test (3)
* GitHub Check: E2E Mobile Test (2)
* GitHub Check: E2E Mobile Test (4)
* GitHub Check: E2E Mobile Test (1)
* GitHub Check: Analyze (typescript, blocksuite)
* GitHub Check: Analyze (javascript, blocksuite)
* GitHub Check: Build AFFiNE native (x86_64-pc-windows-msvc)
* GitHub Check: Analyze (javascript, affine)
* GitHub Check: Lint
* GitHub Check: Analyze (typescript, affine)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T04:32:21Z
user: login: forehalo
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-15T06:42:34Z
user: login: EYHN
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-16T02:24:19Z
user: login: forehalo
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-16T02:36:41Z
user: login: forehalo
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-16T09:37:08Z
user: login: CatsJuice
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-16T10:53:20Z

Review comments:
diff_hunk: @@ -123,18 +135,29 @@ export const AllPage = () => {
       : null
   );
 
+  useEffect(() => {
+    // if selected collection is not in pinned collections, set selected collection id to null
+    if (
+      isPinnedCollectionDataReady &&
+      selectedCollectionId &&
+      !pinnedCollections.find(c => c.collectionId === selectedCollectionId)
path: packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx
user: login: graphite-app[bot]
body: Use `.some()` instead of `.find()` for checking existence. Change `!pinnedCollections.find(c => c.collectionId === selectedCollectionId)` to `!pinnedCollections.some(c => c.collectionId === selectedCollectionId)` to follow the linter's preferred pattern.

*Spotted by [Diamond](https://app.graphite.dev/diamond/?org=toeverything&ref=ai-review-comment) (based on CI logs)*<i class='graphite__hidden'><br /><br />Is this helpful? React 👍 or 👎 to let us know.</i>
created_at: 2025-05-15T04:04:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -19,6 +19,11 @@ export class CollectionService extends Service {
     },
   });
 
+  readonly collectionDataReady$ = LiveData.from(
+    this.store.watchCollectionDataReady(),
+    false
+  );
path: packages/frontend/core/src/modules/collection/services/collection.ts
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Properly exposes collection readiness state.**

This implementation correctly wraps the store's readiness observable as a readonly LiveData property with an appropriate initial value of `false`. This maintains consistency with other reactive properties in the service.


Similar to the pinned collection service, there's an ESLint error about Finnish notation ($ suffix). This appears to be a consistent pattern, but you might want to verify:

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if the Finnish notation is consistently used in this service
echo "Checking for similar patterns in CollectionService:"
cat packages/frontend/core/src/modules/collection/services/collection.ts | grep -E "\$\s*="
```

Length of output: 205

---

🏁 Script executed:

```shell
#!/bin/bash
echo "Scanning for Finnish notation ($ suffix) in CollectionService:"
rg -n --regexp 'readonly \w+\$[[:space:]]*=' packages/frontend/core/src/modules/collection/services/collection.ts
```

Length of output: 394

---



</details>

**CollectionService: Consistent `$`-suffix usage**

The new `collectionDataReady$` property correctly wraps the store’s readiness observable (initializing to `false`) and matches the pattern used elsewhere in this service:

- Lines 22, 28, and 33 define `collectionDataReady$`, `collectionMetas$`, and `collections$` respectively.
- All three use the `$` suffix to denote reactive LiveData streams.

If your ESLint configuration is flagging Finnish-notation (`$` suffix), you’ll need to either disable or adjust that rule for this file (for example, add a file-level `// eslint-disable @yourorg/no-finnish-notation` or narrow the rule’s scope) so that these stream properties remain consistently named.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 ESLint</summary>

[error] 22-22: Finnish notation should not be used here.

(rxjs/finnish)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/frontend/core/src/modules/collection/services/collection.ts around
lines 22 to 25, the ESLint rule flags the use of Finnish notation with the `$`
suffix on reactive LiveData properties like collectionDataReady$. To fix this,
add a file-level ESLint disable comment for the specific rule that disallows
Finnish notation at the top of this file, ensuring consistent naming of reactive
streams without triggering lint errors.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T04:07:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -19,10 +19,23 @@ export class WorkspaceDBTable<
     super();
   }
 
+  isReady$ = LiveData.from(
+    this.workspaceService.workspace.engine.doc
+      .docState$(this.props.storageDocId)
+      .pipe(
+        map(docState => docState.ready),
+        distinctUntilChanged()
+      ),
+    false
+  );
+
   isSyncing$ = LiveData.from(
     this.workspaceService.workspace.engine.doc
       .docState$(this.props.storageDocId)
-      .pipe(map(docState => docState.syncing)),
+      .pipe(
+        map(docState => docState.syncing),
+        distinctUntilChanged()
+      ),
path: packages/frontend/core/src/modules/db/entities/table.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**ESLint “rxjs/finnish” rule is failing – silence it or rename the fields**

The CI pipeline is currently red-flagging both `isReady$` and `isSyncing$` because the `rxjs/finnish` rule believes Finnish notation (`$` suffix) is **not** allowed for non-observable properties.  
`LiveData.from` does return an observable‐like wrapper, so the `$` suffix is justified. Either:

1. Keep the naming and silence the rule:

```diff
-  isReady$ = LiveData.from(
+  // eslint-disable-next-line rxjs/finnish
+  isReady$ = LiveData.from(
     …
-  isSyncing$ = LiveData.from(
+  // eslint-disable-next-line rxjs/finnish
+  isSyncing$ = LiveData.from(
```

2. Or, if your lint configuration truly bans Finnish notation for class properties, rename the fields (e.g. `ready$`, `syncing$`) and update all references.

Failing to address this will keep the linter red.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
  // eslint-disable-next-line rxjs/finnish
  isReady$ = LiveData.from(
    this.workspaceService.workspace.engine.doc
      .docState$(this.props.storageDocId)
      .pipe(
        map(docState => docState.ready),
        distinctUntilChanged()
      ),
    false
  );

  // eslint-disable-next-line rxjs/finnish
  isSyncing$ = LiveData.from(
    this.workspaceService.workspace.engine.doc
      .docState$(this.props.storageDocId)
      .pipe(
        map(docState => docState.syncing),
        distinctUntilChanged()
      ),
```

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 ESLint</summary>

[error] 22-22: Finnish notation should not be used here.

(rxjs/finnish)

---

[error] 32-32: Finnish notation should not be used here.

(rxjs/finnish)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/frontend/core/src/modules/db/entities/table.ts around lines 22 to
38, the properties isReady$ and isSyncing$ are flagged by the ESLint
rxjs/finnish rule because it does not recognize LiveData.from as returning an
observable. To fix this, either add an ESLint disable comment to silence the
rxjs/finnish rule for these lines or rename the properties to remove the $
suffix (e.g., ready$ to ready or syncing$ to syncing) and update all their
references accordingly throughout the codebase.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T04:07:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -303,42 +325,75 @@ export const AllPage = () => {
     });
   }, [docsService.list, openConfirmModal, selectedDocIds, t]);
 
+  const handleSelectCollection = useCallback((collectionId: string) => {
+    setSelectedCollectionId(collectionId);
+    setTempFilters(null);
+  }, []);
+
+  const handleEditCollection = useCallback(
+    (collectionId: string) => {
+      const collection = collectionService.collection$(collectionId).value;
+      if (!collection) {
+        return;
+      }
+      console.log('handleEditCollection', collection.info$.value.rules.filters);
+      setSelectedCollectionId(collectionId);
+      setTempFilters(collection.info$.value.rules.filters);
+    },
+    [collectionService]
+  );
+
   const handleSaveFilters = useCallback(() => {
-    openPromptModal({
-      title: t['com.affine.editCollection.saveCollection'](),
-      label: t['com.affine.editCollectionName.name'](),
-      inputOptions: {
-        placeholder: t['com.affine.editCollectionName.name.placeholder'](),
-      },
-      children: t['com.affine.editCollectionName.createTips'](),
-      confirmText: t['com.affine.editCollection.save'](),
-      cancelText: t['com.affine.editCollection.button.cancel'](),
-      confirmButtonOptions: {
-        variant: 'primary',
-      },
-      onConfirm(name) {
-        const id = collectionService.createCollection({
-          name,
-          rules: {
-            filters: tempFilters,
-          },
-        });
-        pinnedCollectionService.addPinnedCollection({
-          collectionId: id,
-          index: pinnedCollectionService.indexAt('after'),
-        });
-        setTempFilters([]);
-        setSelectedCollectionId(id);
-      },
-    });
+    if (selectedCollectionId) {
+      collectionService.updateCollection(selectedCollectionId, {
+        rules: {
+          filters: tempFilters ?? [],
+        },
+      });
+      setTempFilters(null);
+    } else {
path: packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Persist collection updates asynchronously & surface errors**

`collectionService.updateCollection` appears to be async.  
If it rejects, the UI will silently swallow the error. Making the callback async, awaiting the promise, and catching errors improves robustness:

```diff
-  const handleSaveFilters = useCallback(() => {
+  const handleSaveFilters = useCallback(async () => {
     if (selectedCollectionId) {
-      collectionService.updateCollection(selectedCollectionId, {
+      try {
+        await collectionService.updateCollection(selectedCollectionId, {
           rules: {
             filters: tempFilters ?? [],
           },
-      });
-      setTempFilters(null);
+        });
+        setTempFilters(null);
+      } catch (e) {
+        console.error('Failed to update collection', e);
+        // optional: surface toast / notification here
+      }
```

Same applies in the “create-and-pin” branch inside `openPromptModal`.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
  const handleSaveFilters = useCallback(async () => {
    if (selectedCollectionId) {
      try {
        await collectionService.updateCollection(selectedCollectionId, {
          rules: {
            filters: tempFilters ?? [],
          },
        });
        setTempFilters(null);
      } catch (e) {
        console.error('Failed to update collection', e);
        // optional: surface toast / notification here
      }
    } else {
      // ...
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx
around lines 346 to 354, the call to collectionService.updateCollection is
asynchronous but not awaited, causing errors to be silently swallowed. Modify
handleSaveFilters to be async, await the updateCollection call, and wrap it in a
try-catch block to handle and surface any errors properly. Apply the same
async-await and error handling pattern to the "create-and-pin" branch inside
openPromptModal to ensure consistent error management.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T04:07:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -303,42 +325,75 @@
     });
   }, [docsService.list, openConfirmModal, selectedDocIds, t]);
 
+  const handleSelectCollection = useCallback((collectionId: string) => {
+    setSelectedCollectionId(collectionId);
+    setTempFilters(null);
+  }, []);
+
+  const handleEditCollection = useCallback(
+    (collectionId: string) => {
+      const collection = collectionService.collection$(collectionId).value;
+      if (!collection) {
+        return;
+      }
+      console.log('handleEditCollection', collection.info$.value.rules.filters);
+      setSelectedCollectionId(collectionId);
+      setTempFilters(collection.info$.value.rules.filters);
+    },
+    [collectionService]
+  );
path: packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Remove console.log & guard against undefined filters**

A stray `console.log` will leak to production and `collection.info$.value.rules.filters` can be `undefined`, blowing up `setTempFilters`.

```diff
-      console.log('handleEditCollection', collection.info$.value.rules.filters);
-      setSelectedCollectionId(collectionId);
-      setTempFilters(collection.info$.value.rules.filters);
+      setSelectedCollectionId(collectionId);
+      setTempFilters(collection.info$.value.rules?.filters ?? []);
```

Keeps logs clean and avoids a potential runtime error.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
    (collectionId: string) => {
      const collection = collectionService.collection$(collectionId).value;
      if (!collection) {
        return;
      }
      setSelectedCollectionId(collectionId);
      setTempFilters(collection.info$.value.rules?.filters ?? []);
    },
    [collectionService]
  );
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx
around lines 334 to 344, remove the console.log statement to prevent leaking
logs to production. Additionally, add a guard to check if
collection.info$.value.rules.filters is defined before passing it to
setTempFilters to avoid runtime errors. If filters are undefined, pass an empty
array or a safe default to setTempFilters.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T04:07:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -14,6 +14,11 @@ export class PinnedCollectionService extends Service {
     super();
   }
 
+  pinnedCollectionDataReady$ = LiveData.from(
+    this.pinnedCollectionStore.watchPinnedCollectionDataReady(),
+    false
+  );
path: packages/frontend/core/src/modules/collection/services/pinned-collection.ts
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**New readiness tracking observable looks good, but consider addressing the linting error.**

The addition of `pinnedCollectionDataReady$` observable properly follows the reactive pattern used throughout the codebase and aligns with the existing implementation style. This provides important state tracking for UI components.

However, there's an ESLint error about Finnish notation (the `$` suffix). Since this pattern is used consistently elsewhere in the file (e.g., `pinnedCollections$`), this appears to be a false positive or a misconfigured linting rule.


Consider either:
1. Adding an ESLint disable comment if this is an accepted exception
2. Updating the ESLint configuration if Finnish notation is the preferred style

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if Finnish notation is used consistently in the codebase
cd packages/frontend/core/src
grep -r '\$\s*=' --include="*.ts" . | wc -l
```

Length of output: 81

---



</details>

**Observables With `$` Suffix Trigger False-Positive ESLint Error**

The new `pinnedCollectionDataReady$` observable follows our established reactive pattern, but the ESLint `rxjs/finnish` rule is flagging `LiveData` (a custom type) as non-observable. You can address this in one of two ways:

• Inline disable: add an ESLint disable comment before the declaration  
• ESLint config: update the `rxjs/finnish` rule to recognize `LiveData` as a valid observable type

Locations to update:
- File: packages/frontend/core/src/modules/collection/services/pinned-collection.ts  
  Lines: 17–20

Suggested diff (inline disable):
```diff
  // disable Finnish-notation rule for custom LiveData type
+ // eslint-disable-next-line rxjs/finnish
  pinnedCollectionDataReady$ = LiveData.from(
    this.pinnedCollectionStore.watchPinnedCollectionDataReady(),
    false
  );
```

Or in `.eslintrc.js`, extend `rxjs/finnish`:
```js
"rules": {
  "rxjs/finnish": ["error", {
    "observableTypeNames": ["Observable", "Subject", "LiveData"]
  }]
}
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 ESLint</summary>

[error] 17-17: Finnish notation should not be used here.

(rxjs/finnish)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In packages/frontend/core/src/modules/collection/services/pinned-collection.ts
around lines 17 to 20, the ESLint rxjs/finnish rule incorrectly flags the
pinnedCollectionDataReady$ observable because LiveData is a custom type not
recognized as a valid observable type. Fix this by either adding an ESLint
disable comment immediately before the pinnedCollectionDataReady$ declaration to
suppress the warning inline, or preferably update the ESLint configuration
(e.g., .eslintrc.js) to include "LiveData" in the rxjs/finnish rule's
observableTypeNames array so the rule recognizes it as a valid observable type.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-15T04:32:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -123,18 +135,29 @@ export const AllPage = () => {
       : null
   );
 
+  useEffect(() => {
path: packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx
user: login: forehalo
body: these two effects do not need to be wrapped in useEffect
created_at: 2025-05-15T06:42:34Z
author_association: COLLABORATOR
diff_hunk: @@ -123,18 +135,29 @@ export const AllPage = () => {
       : null
   );
 
+  useEffect(() => {
path: packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx
user: login: EYHN
body: Why? You can only change state in useEffect
created_at: 2025-05-16T02:24:19Z
author_association: MEMBER
diff_hunk: @@ -123,18 +135,29 @@ export const AllPage = () => {
       : null
   );
 
+  useEffect(() => {
path: packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx
user: login: forehalo
body: setState can happen in renderer, but in this case, useEffect may reduce the condition checks. so forget about it
created_at: 2025-05-16T02:36:41Z
author_association: COLLABORATOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-15T04:02:25Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This update introduces reactive readiness tracking for collections and pinned collections, adds UI controls for editing and removing pinned collections, and unifies filter state handling with nullable semantics. New observables and methods are added to services and stores for readiness tracking, and UI components are updated to support new callbacks and styling for enhanced interactivity.

## Changes

| File(s)                                                                                               | Change Summary                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
|-----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx`                          | Added reactive state tracking for collection and pinned collection readiness and data using `useLiveData`. Introduced nullable `tempFilters` state and new callbacks for selecting, editing, and creating filters/collections. Updated filter area rendering and logic to use nullable semantics and synchronize selected collection state with pinned collections and readiness.                                                                                                                                                                      |
| `packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.tsx`                | Enhanced pinned collections UI with edit and remove buttons, added new callbacks `onClickEdit` and `onClickRemove` for pinned collection items. Renamed props for clarity and updated handlers to activate collections appropriately on removal. Renamed pin collection callbacks for consistency. Imported new icons for UI controls.                                                                                                                                                                                   |
| `packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.css.ts`             | Added new CSS styles `itemContent`, `editIconButton`, and `closeButton` to support hover effects, text truncation, and visibility toggling of edit and close buttons within pinned collection items.                                                                                                                                                                                                                                                                                                                            |
| `packages/frontend/core/src/modules/collection/services/collection.ts`                              | Added `collectionDataReady$` observable property to `CollectionService` to track readiness of collection data reactively.                                                                                                                                                                                                                                                                                                                                                                                                    |
| `packages/frontend/core/src/modules/collection/services/pinned-collection.ts`                       | Added `pinnedCollectionDataReady$` observable property to `PinnedCollectionService` to track readiness of pinned collection data reactively.                                                                                                                                                                                                                                                                                                                                                                             |
| `packages/frontend/core/src/modules/collection/stores/collection.ts`                               | Added `watchCollectionDataReady()` method to `CollectionStore` returning an observable of collection data readiness state using the workspace engine document state.                                                                                                                                                                                                                                                                                                                                                       |
| `packages/frontend/core/src/modules/collection/stores/pinned-collection.ts`                        | Added `watchPinnedCollectionDataReady()` method to `PinnedCollectionStore` returning observable readiness state from pinned collections database service.                                                                                                                                                                                                                                                                                                                                                                |
| `packages/frontend/core/src/modules/db/entities/table.ts`                                          | Added `isReady$` observable to `WorkspaceDBTable` to emit readiness state of the workspace engine document. Updated `isSyncing$` observable to include `distinctUntilChanged` for emitting only on state changes.                                                                                                                                                                                                                                                                                                            |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant AllPage
    participant CollectionService
    participant PinnedCollectionService
    participant PinnedCollectionsUI

    User->>AllPage: Selects a pinned collection
    AllPage->>CollectionService: Check collectionDataReady$
    AllPage->>PinnedCollectionService: Check pinnedCollectionDataReady$
    alt If collection or pinned collection is not ready
        AllPage-->>User: Wait for readiness
    else If ready
        AllPage->>PinnedCollectionsUI: Update selected collection
    end

    User->>PinnedCollectionsUI: Clicks edit or remove on a pinned collection
    PinnedCollectionsUI->>AllPage: Calls handleEditCollection or handleRemove
    AllPage->>CollectionService: Update or remove collection as needed
    AllPage->>PinnedCollectionsUI: Update UI state
```

## Poem

> In the meadow of code, collections now gleam,  
> With readiness tracked in a reactive stream.  
> Edit and remove—just a click away,  
> Filters now nullable, guiding the way.  
> Buttons appear with a gentle fade,  
> As rabbits rejoice in the features we've made!  
> 🐇✨

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between a1de033df3ea013c8157e36129e11e12ef088cca and 34686f3d854526b76c983b1616da209f760667e1.

</details>

<details>
<summary>📒 Files selected for processing (8)</summary>

* `packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx` (5 hunks)
* `packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.css.ts` (1 hunks)
* `packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.tsx` (7 hunks)
* `packages/frontend/core/src/modules/collection/services/collection.ts` (1 hunks)
* `packages/frontend/core/src/modules/collection/services/pinned-collection.ts` (1 hunks)
* `packages/frontend/core/src/modules/collection/stores/collection.ts` (2 hunks)
* `packages/frontend/core/src/modules/collection/stores/pinned-collection.ts` (1 hunks)
* `packages/frontend/core/src/modules/db/entities/table.ts` (2 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (5)</summary>

* packages/frontend/core/src/modules/collection/stores/pinned-collection.ts
* packages/frontend/core/src/modules/collection/stores/collection.ts
* packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.css.ts
* packages/frontend/core/src/desktop/pages/workspace/all-page/all-page.tsx
* packages/frontend/core/src/desktop/pages/workspace/all-page/pinned-collections.tsx

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 ESLint</summary>

<details>
<summary>packages/frontend/core/src/modules/collection/services/collection.ts</summary>

[error] 22-22: Finnish notation should not be used here.

(rxjs/finnish)

</details>
<details>
<summary>packages/frontend/core/src/modules/collection/services/pinned-collection.ts</summary>

[error] 17-17: Finnish notation should not be used here.

(rxjs/finnish)

</details>
<details>
<summary>packages/frontend/core/src/modules/db/entities/table.ts</summary>

[error] 22-22: Finnish notation should not be used here.

(rxjs/finnish)

---

[error] 32-32: Finnish notation should not be used here.

(rxjs/finnish)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>packages/frontend/core/src/modules/db/entities/table.ts (2)</summary>

`6-6`: **Good addition of distinctUntilChanged to imports.**

The import has been properly updated to include `distinctUntilChanged`, which will be used to optimize the reactive streams.

---

`35-38`: **Performance improvement with distinctUntilChanged.**

Adding `distinctUntilChanged()` to the `isSyncing$` observable is a great optimization that prevents unnecessary re-renders and processing when the syncing state hasn't changed.

</details>

</blockquote></details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=toeverything/AFFiNE&utm_content=12296):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: EYHN
created_at: 2025-05-15T04:02:32Z
body: * **#12296** <a href="https://app.graphite.dev/github/pr/toeverything/AFFiNE/12296?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>: 2  dependent PRs ([#12303](https://github.com/toeverything/AFFiNE/pull/12303) <a href="https://app.graphite.dev/github/pr/toeverything/AFFiNE/12303?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>, [#12315](https://github.com/toeverything/AFFiNE/pull/12315) <a href="https://app.graphite.dev/github/pr/toeverything/AFFiNE/12315?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>) 👈 <a href="https://app.graphite.dev/github/pr/toeverything/AFFiNE/12296?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* `canary`
<hr />

## How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:
- <kbd>merge</kbd> - adds this PR to the back of the merge queue
- <kbd>hotfix</kbd> - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using [this link](https://app.graphite.dev/invite/github/toeverything?ref=merge-queue-instructions-in-stack-comment&prId=12296).

<h2></h2>
<sub>

An organization admin has enabled the [Graphite Merge Queue](https://graphite.dev/docs/graphite-merge-queue) in this repository.

</sub>

<sub>

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

</sub>

<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->
author_association: MEMBER
user: login: codecov[bot]
created_at: 2025-05-15T04:08:00Z
body: ## [Codecov](https://app.codecov.io/gh/toeverything/AFFiNE/pull/12296?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toeverything) Report
Attention: Patch coverage is `0%` with `2 lines` in your changes missing coverage. Please review.
> Project coverage is 55.14%. Comparing base [(`1e77749`)](https://app.codecov.io/gh/toeverything/AFFiNE/commit/1e7774929cc27b210cf61e5345a864441795d699?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toeverything) to head [(`34686f3`)](https://app.codecov.io/gh/toeverything/AFFiNE/commit/34686f3d854526b76c983b1616da209f760667e1?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toeverything).
> Report is 1 commits behind head on canary.

| [Files with missing lines](https://app.codecov.io/gh/toeverything/AFFiNE/pull/12296?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toeverything) | Patch % | Lines |
|---|---|---|
| [...ges/frontend/core/src/modules/db/entities/table.ts](https://app.codecov.io/gh/toeverything/AFFiNE/pull/12296?src=pr&el=tree&filepath=packages%2Ffrontend%2Fcore%2Fsrc%2Fmodules%2Fdb%2Fentities%2Ftable.ts&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toeverything#diff-cGFja2FnZXMvZnJvbnRlbmQvY29yZS9zcmMvbW9kdWxlcy9kYi9lbnRpdGllcy90YWJsZS50cw==) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/toeverything/AFFiNE/pull/12296?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toeverything) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           canary   #12296      +/-   ##
==========================================
- Coverage   55.18%   55.14%   -0.04%     
==========================================
  Files        2620     2620              
  Lines      121945   121946       +1     
  Branches    19454    19454              
==========================================
- Hits        67291    67245      -46     
- Misses      52679    53177     +498     
+ Partials     1975     1524     -451     
```

| [Flag](https://app.codecov.io/gh/toeverything/AFFiNE/pull/12296/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toeverything) | Coverage Δ | |
|---|---|---|
| [server-test](https://app.codecov.io/gh/toeverything/AFFiNE/pull/12296/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toeverything) | `78.49% <ø> (-0.01%)` | :arrow_down: |
| [unittest](https://app.codecov.io/gh/toeverything/AFFiNE/pull/12296/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toeverything) | `31.25% <0.00%> (-0.07%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toeverything#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/toeverything/AFFiNE/pull/12296?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toeverything).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toeverything).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: graphite-app[bot]
created_at: 2025-05-16T09:40:04Z
body: ### Merge activity

* **May 16, 5:40 AM EDT**: `EYHN` added this pull request to the [Graphite merge queue](https://app.graphite.dev/merges?org=toeverything&repo=AFFiNE).
* **May 16, 5:47 AM EDT**: `EYHN`  removed this pull request from the [Graphite merge queue](https://app.graphite.dev/merges?org=toeverything&repo=AFFiNE).
* **May 19, 12:24 AM EDT**: `EYHN` added this pull request to the [Graphite merge queue](https://app.graphite.dev/merges?org=toeverything&repo=AFFiNE).
* **May 19, 12:38 AM EDT**: Merged by the [Graphite merge queue](https://app.graphite.dev/merges?org=toeverything&repo=AFFiNE).

author_association: CONTRIBUTOR