Compare commits

..

19 Commits

Author SHA1 Message Date
DarkSky
3a13bdcc3d fix: ci 2025-11-16 00:43:30 +08:00
Mau Nguyen
71d682c1c0 feat(editor): add font size adjustment in editor settings (#13549)
Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
Co-authored-by: DarkSky <darksky2048@gmail.com>
2025-11-15 23:57:17 +08:00
DarkSky
d90eeffe84 fix: ci 2025-11-15 23:52:14 +08:00
DarkSky
12fe7a5ced fix: flatpak build 2025-11-15 22:01:24 +08:00
Roger Clotet
a35bcdc3ef feat(i18n): add missing catalan strings (#13914)
I'm a native Catalan and Spanish speaker. 

There are lots of changes, if I notice something is off when using it,
I'll submit more patches to improve it.

One decision I'm not sure about is keeping "edgeless" as the
translation, since it's difficult to find a proper way of saying it
that's not too similar to "borderless" or "without borders" in Catalan.
I noticed Spanish has some of the strings as "Edgeless" and others "sin
bordes", so I'm guessing this is a bit subjective and in my opinion
leaving it unchanged is easier to understand than if poorly translated.
2025-11-15 21:27:05 +08:00
DarkSky
29cb937493 feat(native): add events feature for yocto 2025-11-15 21:17:02 +08:00
AKY
e52bcb7fd6 feat(i18n): korean translation (#13733)
Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
2025-11-15 19:31:19 +08:00
DarkSky
4e082e4170 chore: bump version 2025-11-15 17:29:54 +08:00
DarkSky
46958867ef fix(editor): mermaid style pollution (#13950)
fix #13546
2025-11-15 17:26:08 +08:00
DarkSky
beb09300b9 fix(editor): date picker in year (#13948)
fix #13582
2025-11-15 16:56:24 +08:00
github-actions[bot]
f644454c5c chore(i18n): sync translations (#13949)
New Crowdin translations by [Crowdin GH
Action](https://github.com/crowdin/github-action)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-11-15 16:30:48 +08:00
DarkSky
1654d8efe4 feat: improve sub sync (#13932) 2025-11-15 15:52:35 +08:00
DarkSky
46e7d9fab7 chore: bump electron (#13935)
fix #13647

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

## Release Notes

* **Chores**
* Updated development tooling and build dependencies to latest stable
versions
  * Increased minimum Node.js requirement to version 22
  * Updated macOS deployment target to version 11.6
  * Enhanced type safety and error handling in build processes
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-15 15:52:04 +08:00
Xun Sun
17ec76540b feat(editor): import docs from docx (#11774)
Support importing .docx files, as mentioned in
https://github.com/toeverything/AFFiNE/issues/10154#issuecomment-2655744757

It essentially uses mammoth to convert the docx to html, and then
imports the html with the standard steps.

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

* **New Features**
* Import Microsoft Word (.docx) files directly via the import dialog
(creates new documents).
* .docx added as a selectable file type in the file picker and import
options.

* **Localization**
* Added localized labels and tooltips for DOCX import in English,
Simplified Chinese, and Traditional Chinese.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
Co-authored-by: DarkSky <darksky2048@gmail.com>
2025-11-15 15:51:23 +08:00
DarkSky
e5db0e66c1 chore: update schema 2025-11-15 14:45:18 +08:00
Kevin
525b65c6ca fix(server): config typo (#13913)
Fix a typo on the SMTP configuration page
2025-11-15 14:41:03 +08:00
DarkSky
c302425a05 fix(server): doc squash timeout (#13939) 2025-11-15 03:19:51 +08:00
DarkSky
abe73e9996 fix: config escape error (#13936)
fix #13702
2025-11-14 23:24:44 +08:00
Richard Lora
e379da200e feat(editor): add collapse/expand toggle for groups with caching (#12671)
https://github.com/user-attachments/assets/4ef71704-57bb-45b8-9e73-8a51c67fb158

Adds a collapsible toggle for group-by groups.

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

- **New Features**
- Collapsible groups for desktop and mobile table views with persistent
per-view collapsed state and a keyboard-accessible toggle button.

- **Bug Fixes**
  - Group title icons now render consistently across variants.

- **Tests**
- Added unit tests verifying collapse/expand behavior for group
components.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: 3720 <zuozijian1994@gmail.com>
Co-authored-by: L-Sun <zover.v@gmail.com>
2025-11-14 04:21:13 +00:00
166 changed files with 5775 additions and 1582 deletions

View File

@@ -222,12 +222,12 @@
},
"SMTP.sender": {
"type": "string",
"description": "Sender of all the emails (e.g. \"AFFiNE Team <noreply@affine.pro>\")\n@default \"\"\n@environment `MAILER_SENDER`",
"default": ""
"description": "Sender of all the emails (e.g. \"AFFiNE Self Hosted <noreply@example.com>\")\n@default \"AFFiNE Self Hosted <noreply@example.com>\"\n@environment `MAILER_SENDER`",
"default": "AFFiNE Self Hosted <noreply@example.com>"
},
"SMTP.ignoreTLS": {
"type": "boolean",
"description": "Whether ignore email server's TSL certification verification. Enable it for self-signed certificates.\n@default false\n@environment `MAILER_IGNORE_TLS`",
"description": "Whether ignore email server's TLS certificate verification. Enable it for self-signed certificates.\n@default false\n@environment `MAILER_IGNORE_TLS`",
"default": false
},
"fallbackDomains": {
@@ -262,12 +262,12 @@
},
"fallbackSMTP.sender": {
"type": "string",
"description": "Sender of all the emails (e.g. \"AFFiNE Team <noreply@affine.pro>\")\n@default \"\"",
"description": "Sender of all the emails (e.g. \"AFFiNE Self Hosted <noreply@example.com>\")\n@default \"\"",
"default": ""
},
"fallbackSMTP.ignoreTLS": {
"type": "boolean",
"description": "Whether ignore email server's TSL certification verification. Enable it for self-signed certificates.\n@default false",
"description": "Whether ignore email server's TLS certificate verification. Enable it for self-signed certificates.\n@default false",
"default": false
}
}

View File

@@ -3,4 +3,4 @@ name: affine
description: AFFiNE cloud chart
type: application
version: 0.0.0
appVersion: "0.25.2"
appVersion: "0.25.5"

View File

@@ -3,7 +3,7 @@ name: doc
description: AFFiNE doc server
type: application
version: 0.0.0
appVersion: "0.25.2"
appVersion: "0.25.5"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -3,7 +3,7 @@ name: graphql
description: AFFiNE GraphQL server
type: application
version: 0.0.0
appVersion: "0.25.2"
appVersion: "0.25.5"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -3,7 +3,7 @@ name: renderer
description: AFFiNE renderer server
type: application
version: 0.0.0
appVersion: "0.25.2"
appVersion: "0.25.5"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -3,7 +3,7 @@ name: sync
description: AFFiNE Sync Server
type: application
version: 0.0.0
appVersion: "0.25.2"
appVersion: "0.25.5"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -19,7 +19,7 @@ env:
APP_NAME: affine
AFFINE_ENV: dev
COVERAGE: true
MACOSX_DEPLOYMENT_TARGET: '10.13'
MACOSX_DEPLOYMENT_TARGET: '11.6'
DEPLOYMENT_TYPE: affine
AFFINE_INDEXER_ENABLED: true

View File

@@ -25,7 +25,7 @@ env:
RELEASE_VERSION: ${{ inputs.app-version }}
DEBUG: 'affine:*,napi:*'
APP_NAME: affine
MACOSX_DEPLOYMENT_TARGET: '10.13'
MACOSX_DEPLOYMENT_TARGET: '11.6'
jobs:
before-make:
@@ -165,7 +165,7 @@ jobs:
mv packages/frontend/apps/electron/out/*/make/zip/linux/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.zip
mv packages/frontend/apps/electron/out/*/make/*.AppImage ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.appimage
mv packages/frontend/apps/electron/out/*/make/deb/${{ matrix.spec.arch }}/*.deb ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.deb
mv packages/frontend/apps/electron/out/*/make/flatpak/*/*.flatpak ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.flatpak
# mv packages/frontend/apps/electron/out/*/make/flatpak/*/*.flatpak ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.flatpak
- uses: actions/attest-build-provenance@v2
if: ${{ matrix.spec.platform == 'darwin' }}
@@ -466,6 +466,4 @@ jobs:
draft: ${{ inputs.build-type == 'stable' }}
prerelease: ${{ inputs.build-type != 'stable' }}
tag_name: v${{ env.RELEASE_VERSION}}
files: |
./release/*
./release/.env.example
files: ./release/*

View File

@@ -296,7 +296,7 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2",
"version": "0.25.5",
"devDependencies": {
"@vanilla-extract/vite-plugin": "^5.0.0",
"msw": "^2.8.4",

View File

@@ -41,5 +41,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -48,5 +48,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -48,5 +48,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -39,5 +39,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -43,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -49,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -49,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -49,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -46,7 +46,7 @@
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"dompurify": "^3.2.4",
"dompurify": "^3.3.0",
"html2canvas": "^1.4.1",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
@@ -67,5 +67,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -82,5 +82,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -229,9 +229,9 @@ export class DatePicker extends WithDisposable(LitElement) {
private _modeDecade(offset: number) {
this._yearCursor = clamp(
this._yearCursor + offset,
this._minYear,
this._maxYear,
this._yearCursor + offset
this._maxYear
);
this._getYearMatrix();
}

View File

@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -0,0 +1,36 @@
import { describe, expect, test } from 'vitest';
import { mobileEffects } from '../view-presets/table/mobile/effect.js';
import type { MobileTableGroup } from '../view-presets/table/mobile/group.js';
import { pcEffects } from '../view-presets/table/pc/effect.js';
import type { TableGroup } from '../view-presets/table/pc/group.js';
/** @vitest-environment happy-dom */
describe('TableGroup', () => {
test('toggle collapse on pc', () => {
pcEffects();
const group = document.createElement(
'affine-data-view-table-group'
) as TableGroup;
expect(group.collapsed$.value).toBe(false);
(group as any)._toggleCollapse();
expect(group.collapsed$.value).toBe(true);
(group as any)._toggleCollapse();
expect(group.collapsed$.value).toBe(false);
});
test('toggle collapse on mobile', () => {
mobileEffects();
const group = document.createElement(
'mobile-table-group'
) as MobileTableGroup;
expect(group.collapsed$.value).toBe(false);
(group as any)._toggleCollapse();
expect(group.collapsed$.value).toBe(true);
(group as any)._toggleCollapse();
expect(group.collapsed$.value).toBe(false);
});
});

View File

@@ -26,13 +26,10 @@ const GroupTitleMobile = (
const type = groupData.tType;
if (!type) return nothing;
const icon =
groupData.value == null
? ''
: html` <uni-lit
class="group-header-icon"
.uni="${groupData.property.icon}"
></uni-lit>`;
const icon = html` <uni-lit
class="group-header-icon"
.uni="${groupData.property.icon}"
></uni-lit>`;
const props: GroupRenderProps = {
group: groupData,
readonly: ops.readonly,
@@ -126,13 +123,10 @@ export const GroupTitle = (
const type = groupData.tType;
if (!type) return nothing;
const icon =
groupData.value == null
? ''
: html` <uni-lit
class="group-header-icon"
.uni="${groupData.property.icon}"
></uni-lit>`;
const icon = html` <uni-lit
class="group-header-icon"
.uni="${groupData.property.icon}"
></uni-lit>`;
const props: GroupRenderProps = {
group: groupData,
readonly: ops.readonly,

View File

@@ -0,0 +1,44 @@
/**
* Shared utility for managing table group collapsed state in sessionStorage.
* Used by both PC and mobile table group implementations.
*/
/**
* Gets the collapsed state for a specific table group from sessionStorage.
* @param viewId - The ID of the table view
* @param groupKey - The key of the group
* @returns The collapsed state as a boolean, or false if not found or invalid
*/
export function getCollapsedState(viewId: string, groupKey: string): boolean {
try {
const value = sessionStorage.getItem(
`affine:table-group:${viewId}:${groupKey}:collapsed`
);
if (!value) return false;
const parsed = JSON.parse(value);
return typeof parsed === 'boolean' ? parsed : false;
} catch {
return false;
}
}
/**
* Sets the collapsed state for a specific table group in sessionStorage.
* @param viewId - The ID of the table view
* @param groupKey - The key of the group
* @param collapsed - The collapsed state to store
*/
export function setCollapsedState(
viewId: string,
groupKey: string,
collapsed: boolean
): void {
try {
sessionStorage.setItem(
`affine:table-group:${viewId}:${groupKey}:collapsed`,
JSON.stringify(collapsed)
);
} catch {
// ignore
}
}

View File

@@ -4,16 +4,22 @@ import {
popupTargetFromElement,
} from '@blocksuite/affine-components/context-menu';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
import { PlusIcon } from '@blocksuite/icons/lit';
import {
PlusIcon,
ToggleDownIcon,
ToggleRightIcon,
} from '@blocksuite/icons/lit';
import { ShadowlessElement } from '@blocksuite/std';
import { signal } from '@preact/signals-core';
import { cssVarV2 } from '@toeverything/theme/v2';
import { css, html, unsafeCSS } from 'lit';
import { css, html, nothing, unsafeCSS } from 'lit';
import { property } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { GroupTitle } from '../../../core/group-by/group-title.js';
import type { Group } from '../../../core/group-by/trait.js';
import type { Row } from '../../../core/index.js';
import { getCollapsedState, setCollapsedState } from '../collapsed-state.js';
import { LEFT_TOOL_BAR_WIDTH } from '../consts.js';
import type { MobileTableViewUILogic } from './table-view-ui-logic.js';
@@ -42,6 +48,28 @@ const styles = css`
line-height: 20px;
color: var(--affine-text-secondary-color);
}
.group-toggle-btn {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
cursor: pointer;
transition: background-color 150ms cubic-bezier(0.42, 0, 1, 1);
}
.group-toggle-btn:hover {
background: var(--affine-hover-color);
}
.group-toggle-btn svg {
width: 16px;
height: 16px;
flex-shrink: 0;
user-select: none;
}
`;
export class MobileTableGroup extends SignalWatcher(
@@ -49,6 +77,29 @@ export class MobileTableGroup extends SignalWatcher(
) {
static override styles = styles;
collapsed$ = signal(false);
private storageLoaded = false;
private _loadCollapsedState() {
if (this.storageLoaded) return;
this.storageLoaded = true;
const view = this.tableViewLogic?.view;
if (!view) return;
const value = getCollapsedState(view.id, this.group?.key ?? 'all');
this.collapsed$.value = value;
}
private readonly _toggleCollapse = (e?: MouseEvent) => {
e?.stopPropagation();
const next = !this.collapsed$.value;
this.collapsed$.value = next;
const view = this.tableViewLogic?.view;
if (view) {
setCollapsedState(view.id, this.group?.key ?? 'all', next);
}
};
private readonly clickAddRow = () => {
this.view.rowAdd('end', this.group?.key);
this.requestUpdate();
@@ -93,6 +144,27 @@ export class MobileTableGroup extends SignalWatcher(
<div
style="position: sticky;left: 0;width: max-content;padding: 6px 0;margin-bottom: 4px;display:flex;align-items:center;gap: 12px;max-width: 400px"
>
<div
class=${`group-toggle-btn ${this.collapsed$.value ? '' : 'expanded'}`}
role="button"
aria-expanded=${this.collapsed$.value ? 'false' : 'true'}
aria-label=${this.collapsed$.value
? 'Expand group'
: 'Collapse group'}
tabindex="0"
@click=${this._toggleCollapse}
@keydown=${(e: KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this._toggleCollapse();
}
}}
>
${this.collapsed$.value
? ToggleRightIcon({ width: '16px', height: '16px' })
: ToggleDownIcon({ width: '16px', height: '16px' })}
</div>
${GroupTitle(this.group, {
readonly: this.view.readonly$.value,
clickAdd: this.clickAddRowInStart,
@@ -109,7 +181,6 @@ export class MobileTableGroup extends SignalWatcher(
private renderRows(rows: Row[]) {
return html`
<mobile-table-header
.renderGroupHeader="${this.renderGroupHeader}"
.tableViewManager="${this.view}"
></mobile-table-header>
<div class="mobile-affine-table-body">
@@ -144,8 +215,26 @@ export class MobileTableGroup extends SignalWatcher(
`;
}
override willUpdate(changed: Map<PropertyKey, unknown>): void {
super.willUpdate(changed);
if (
!this.storageLoaded &&
(changed.has('group') || changed.has('tableViewLogic'))
) {
this._loadCollapsedState();
}
}
override connectedCallback(): void {
super.connectedCallback();
this._loadCollapsedState();
}
override render() {
return this.renderRows(this.rows);
return html`
${this.collapsed$.value ? this.renderGroupHeader() : nothing}
${this.collapsed$.value ? nothing : this.renderRows(this.rows)}
`;
}
@property({ attribute: false })

View File

@@ -57,12 +57,12 @@ const styles = css`
margin-inline: 5px;
}
.label {
.stats-cell .label {
text-transform: uppercase;
color: var(--affine-text-secondary-color);
}
.value {
.stats-cell .value {
color: var(--affine-text-primary-color);
}
`;

View File

@@ -4,11 +4,15 @@ import {
popupTargetFromElement,
} from '@blocksuite/affine-components/context-menu';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
import { PlusIcon } from '@blocksuite/icons/lit';
import {
PlusIcon,
ToggleDownIcon,
ToggleRightIcon,
} from '@blocksuite/icons/lit';
import { ShadowlessElement } from '@blocksuite/std';
import { effect } from '@preact/signals-core';
import { effect, signal } from '@preact/signals-core';
import { cssVarV2 } from '@toeverything/theme/v2';
import { css, html, unsafeCSS } from 'lit';
import { css, html, nothing, unsafeCSS } from 'lit';
import { property, query } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
@@ -18,6 +22,7 @@ import type { Row } from '../../../core/index.js';
import { createDndContext } from '../../../core/utils/wc-dnd/dnd-context.js';
import { defaultActivators } from '../../../core/utils/wc-dnd/sensors/index.js';
import { linearMove } from '../../../core/utils/wc-dnd/utils/linear-move.js';
import { getCollapsedState, setCollapsedState } from '../collapsed-state.js';
import { LEFT_TOOL_BAR_WIDTH } from '../consts.js';
import { TableViewAreaSelection } from '../selection';
import { DataViewColumnPreview } from './header/column-renderer.js';
@@ -30,6 +35,12 @@ const styles = css`
opacity: 1;
}
affine-data-view-table-group {
margin-top: 4px;
padding-top: 4px;
border-top: 1px solid var(--affine-border-color);
}
.data-view-table-group-add-row {
display: flex;
width: 100%;
@@ -42,6 +53,10 @@ const styles = css`
border-bottom: 1px solid ${unsafeCSS(cssVarV2.layer.insideBorder.border)};
}
.affine-data-view-table-group:hover svg {
fill: var(--affine-icon-color);
}
@media print {
.data-view-table-group-add-row {
display: none;
@@ -60,6 +75,28 @@ const styles = css`
line-height: 20px;
color: var(--affine-text-secondary-color);
}
.group-toggle-btn {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
cursor: pointer;
transition: background-color 150ms cubic-bezier(0.42, 0, 1, 1);
}
.group-toggle-btn:hover {
background: var(--affine-hover-color);
}
.group-toggle-btn svg {
width: 16px;
height: 16px;
flex-shrink: 0;
user-select: none;
}
`;
export class TableGroup extends SignalWatcher(
@@ -67,6 +104,29 @@ export class TableGroup extends SignalWatcher(
) {
static override styles = styles;
collapsed$ = signal(false);
private storageLoaded = false;
private _loadCollapsedState() {
if (this.storageLoaded) return;
this.storageLoaded = true;
const view = this.tableViewLogic?.view;
if (!view) return;
const value = getCollapsedState(view.id, this.group?.key ?? 'all');
this.collapsed$.value = value;
}
private readonly _toggleCollapse = (e?: MouseEvent) => {
e?.stopPropagation();
const next = !this.collapsed$.value;
this.collapsed$.value = next;
const view = this.tableViewLogic?.view;
if (view) {
setCollapsedState(view.id, this.group?.key ?? 'all', next);
}
};
private readonly clickAddRow = () => {
this.view.rowAdd('end', this.group?.key);
const selectionController = this.tableViewLogic.selectionController;
@@ -137,10 +197,32 @@ export class TableGroup extends SignalWatcher(
if (!this.group) {
return null;
}
return html`
<div
style="position: sticky;left: 0;width: max-content;padding: 6px 0;margin-bottom: 4px;display:flex;align-items:center;gap: 12px;max-width: 400px"
style="position: sticky;left: 0;width: max-content;padding: 6px 0;margin-bottom: 4px;display:flex;align-items:center;gap: 8px;max-width: 400px"
>
<div
class=${`group-toggle-btn ${this.collapsed$.value ? '' : 'expanded'}`}
role="button"
aria-expanded=${this.collapsed$.value ? 'false' : 'true'}
aria-label=${this.collapsed$.value
? 'Expand group'
: 'Collapse group'}
tabindex="0"
@click=${this._toggleCollapse}
@keydown=${(e: KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this._toggleCollapse();
}
}}
>
${this.collapsed$.value
? ToggleRightIcon({ width: '16px', height: '16px' })
: ToggleDownIcon({ width: '16px', height: '16px' })}
</div>
${GroupTitle(this.group, {
readonly: this.view.readonly$.value,
clickAdd: this.clickAddRowInStart,
@@ -244,8 +326,8 @@ export class TableGroup extends SignalWatcher(
private renderRows(rows: Row[]) {
return html`
<affine-database-column-header
.renderGroupHeader="${this.renderGroupHeader}"
.tableViewLogic="${this.tableViewLogic}"
.renderGroupHeader=${this.renderGroupHeader}
.tableViewLogic=${this.tableViewLogic}
></affine-database-column-header>
<div class="affine-database-block-rows">
${repeat(
@@ -284,13 +366,27 @@ export class TableGroup extends SignalWatcher(
`;
}
override willUpdate(changed: Map<PropertyKey, unknown>): void {
super.willUpdate(changed);
if (
!this.storageLoaded &&
(changed.has('group') || changed.has('tableViewLogic'))
) {
this._loadCollapsedState();
}
}
override connectedCallback(): void {
super.connectedCallback();
this._loadCollapsedState();
this.showIndicator();
}
override render() {
return this.renderRows(this.rows);
return html`
${this.collapsed$.value ? this.renderGroupHeader() : nothing}
${this.collapsed$.value ? nothing : this.renderRows(this.rows)}
`;
}
@query('.affine-database-block-rows')

View File

@@ -57,12 +57,12 @@ const styles = css`
margin-inline: 5px;
}
.label {
.stats-cell .label {
text-transform: uppercase;
color: var(--affine-text-secondary-color);
}
.value {
.stats-cell .value {
color: var(--affine-text-primary-color);
}
`;

View File

@@ -26,5 +26,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -35,5 +35,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -40,5 +40,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -41,5 +41,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -43,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -51,5 +51,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -43,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -25,5 +25,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -47,5 +47,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -50,5 +50,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -56,5 +56,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -43,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -30,5 +30,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -41,5 +41,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -24,7 +24,7 @@
"@types/lodash-es": "^4.17.12",
"@types/mdast": "^4.0.4",
"bytes": "^3.1.2",
"dompurify": "^3.2.4",
"dompurify": "^3.3.0",
"fractional-indexing": "^3.2.0",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
@@ -75,5 +75,5 @@
"devDependencies": {
"vitest": "3.1.3"
},
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -20,6 +20,7 @@ import {
type ToDocSnapshotPayload,
type Transformer,
} from '@blocksuite/store';
import DOMPurify from 'dompurify';
import type { Root } from 'hast';
import rehypeParse from 'rehype-parse';
import rehypeStringify from 'rehype-stringify';
@@ -297,7 +298,8 @@ export class HtmlAdapter extends BaseAdapter<Html> {
override async toDocSnapshot(
payload: ToDocSnapshotPayload<string>
): Promise<DocSnapshot> {
const htmlAst = this._htmlToAst(payload.file);
const sanitized = DOMPurify.sanitize(payload.file);
const htmlAst = this._htmlToAst(sanitized);
const titleAst = HastUtils.querySelector(htmlAst, 'title');
const blockSnapshotRoot = {
type: 'block',

View File

@@ -92,6 +92,13 @@ const FileTypes: NonNullable<OpenFilePickerOptions['types']> = [
'application/zip': ['.zip'],
},
},
{
description: 'Docx',
accept: {
'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
['.docx'],
},
},
{
description: 'MindMap',
accept: {
@@ -111,6 +118,7 @@ type AcceptTypes =
| 'Markdown'
| 'Html'
| 'Zip'
| 'Docx'
| 'MindMap';
export async function openFilesWith(

View File

@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -34,5 +34,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -36,5 +36,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -40,5 +40,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -38,5 +38,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -36,5 +36,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -34,5 +34,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -55,5 +55,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -28,6 +28,7 @@
"fflate": "^0.8.2",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
"mammoth": "^1.11.0",
"rxjs": "^7.8.1",
"zod": "^3.23.8"
},
@@ -41,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -0,0 +1,47 @@
import type { ExtensionType, Schema, Workspace } from '@blocksuite/store';
// @ts-ignore
import { convertToHtml } from 'mammoth/mammoth.browser';
import { HtmlTransformer } from './html';
type ImportDocxOptions = {
collection: Workspace;
schema: Schema;
imported: Blob;
extensions: ExtensionType[];
};
/**
* Imports a .docx file into a doc.
*
* @param options - The import options.
* @param options.collection - The target doc collection.
* @param options.schema - The schema of the target doc collection.
* @param options.imported - The .docx file as a Blob.
* @returns A Promise that resolves to the ID of the newly created doc, or undefined if import fails.
*/
async function importDocx({
collection,
schema,
imported,
extensions,
}: ImportDocxOptions) {
try {
const { value } = await convertToHtml({
arrayBuffer: await imported.arrayBuffer(),
});
return await HtmlTransformer.importHTMLToDoc({
collection,
schema,
html: value,
extensions,
});
} catch (e) {
console.error('Failed to import .docx file:', e);
return undefined;
}
}
export const DocxTransformer = {
importDocx,
};

View File

@@ -1,3 +1,4 @@
export { DocxTransformer } from './docx.js';
export { HtmlTransformer } from './html.js';
export { MarkdownTransformer } from './markdown.js';
export { NotionHtmlTransformer } from './notion-html.js';

View File

@@ -37,5 +37,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -37,5 +37,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -35,5 +35,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -30,5 +30,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -36,5 +36,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -38,5 +38,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -35,5 +35,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -17,5 +17,5 @@
"dependencies": {
"@blocksuite/affine": "workspace:*"
},
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -64,5 +64,5 @@
"devDependencies": {
"vitest": "3.1.3"
},
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -19,7 +19,7 @@
"@preact/signals-core": "^1.8.0",
"@types/hast": "^3.0.4",
"@types/lodash-es": "^4.17.12",
"dompurify": "^3.2.4",
"dompurify": "^3.3.0",
"fractional-indexing": "^3.2.0",
"lib0": "^0.2.97",
"lit": "^3.2.0",
@@ -47,5 +47,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -42,5 +42,5 @@
"!dist/__tests__",
"shim.d.ts"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -33,5 +33,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -46,5 +46,5 @@
"vite-plugin-wasm": "^3.4.1",
"vitest": "3.1.3"
},
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -46,5 +46,5 @@
"vite-plugin-wasm": "^3.3.0",
"vite-plugin-web-components-hmr": "^0.1.3"
},
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -19,5 +19,5 @@
],
"ext": "ts,md,json"
},
"version": "0.25.2"
"version": "0.25.5"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@affine/monorepo",
"version": "0.25.2",
"version": "0.25.5",
"private": true,
"author": "toeverything",
"license": "MIT",
@@ -68,7 +68,7 @@
"@vitest/coverage-istanbul": "3.1.3",
"@vitest/ui": "3.1.3",
"cross-env": "^7.0.3",
"electron": "^35.0.0",
"electron": "^36.0.0",
"eslint": "^9.16.0",
"eslint-config-prettier": "^10.0.0",
"eslint-import-resolver-typescript": "^4.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@affine/server-native",
"version": "0.25.2",
"version": "0.25.5",
"engines": {
"node": ">= 10.16.0 < 11 || >= 11.8.0"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@affine/server",
"private": true,
"version": "0.25.2",
"version": "0.25.5",
"description": "Affine Node.js server",
"type": "module",
"bin": {

View File

@@ -79,11 +79,15 @@ export abstract class DocStorageAdapter extends Connection {
const updates = await this.getDocUpdates(spaceId, docId);
if (updates.length) {
const docUpdate = await this.squash(
snapshot ? [snapshot, ...updates] : updates
);
return await this.squashUpdatesToSnapshot(
spaceId,
docId,
updates,
snapshot
snapshot,
docUpdate
);
}
@@ -95,15 +99,14 @@ export abstract class DocStorageAdapter extends Connection {
spaceId: string,
docId: string,
updates: DocUpdate[],
snapshot: DocRecord | null
snapshot: DocRecord | null,
finalUpdate: DocUpdate
) {
this.logger.log(
`Squashing updates, spaceId: ${spaceId}, docId: ${docId}, updates: ${updates.length}`
);
const { timestamp, bin, editor } = await this.squash(
snapshot ? [snapshot, ...updates] : updates
);
const { bin, timestamp, editor } = finalUpdate;
const newSnapshot: DocRecord = {
spaceId,
docId,

View File

@@ -56,12 +56,12 @@ defineModuleConfig('mailer', {
env: 'MAILER_PASSWORD',
},
'SMTP.sender': {
desc: 'Sender of all the emails (e.g. "AFFiNE Team <noreply@affine.pro>")',
default: '',
desc: 'Sender of all the emails (e.g. "AFFiNE Self Hosted \<noreply@example.com\>")',
default: 'AFFiNE Self Hosted <noreply@example.com>',
env: 'MAILER_SENDER',
},
'SMTP.ignoreTLS': {
desc: "Whether ignore email server's TSL certification verification. Enable it for self-signed certificates.",
desc: "Whether ignore email server's TLS certificate verification. Enable it for self-signed certificates.",
default: false,
env: ['MAILER_IGNORE_TLS', 'boolean'],
},
@@ -92,11 +92,11 @@ defineModuleConfig('mailer', {
default: '',
},
'fallbackSMTP.sender': {
desc: 'Sender of all the emails (e.g. "AFFiNE Team <noreply@affine.pro>")',
desc: 'Sender of all the emails (e.g. "AFFiNE Self Hosted \<noreply@example.com\>")',
default: '',
},
'fallbackSMTP.ignoreTLS': {
desc: "Whether ignore email server's TSL certification verification. Enable it for self-signed certificates.",
desc: "Whether ignore email server's TLS certificate verification. Enable it for self-signed certificates.",
default: false,
},
});

View File

@@ -112,6 +112,10 @@ export const Subscription = z.object({
duration: z.string().nullable(),
});
const IdentifyUserResponse = z.object({
was_created: z.boolean(),
});
export type Subscription = z.infer<typeof Subscription>;
type Entitlement = z.infer<typeof zRcV2RawEntitlementItem>;
type Product = z.infer<typeof zRcV2RawProduct>;
@@ -139,6 +143,41 @@ export class RevenueCatService {
return id;
}
async identifyUser(userId: string, newUserId: string): Promise<boolean> {
try {
const res = await fetch(
`https://api.revenuecat.com/v1/subscribers/identify`,
{
method: 'POST',
body: JSON.stringify({
app_user_id: userId,
new_app_user_id: newUserId,
}),
headers: {
Authorization: `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
}
);
const json = await res.json();
const parsed = IdentifyUserResponse.safeParse(json);
if (parsed.success) {
return parsed.data.was_created;
} else {
this.logger.error(
`RevenueCat identifyUser parse failed: ${JSON.stringify(
parsed.error.format()
)}`
);
return false;
}
} catch (e: any) {
this.logger.error(`RevenueCat identifyUser failed: ${e.message}`);
return false;
}
}
async getProducts(ent: Entitlement): Promise<Product[] | null> {
if (ent.products?.items && ent.products.items.length > 0) {
return ent.products.items;
@@ -182,7 +221,10 @@ export class RevenueCatService {
return null;
}
async getCustomerAlias(customerId: string): Promise<string[] | null> {
async getCustomerAlias(
customerId: string,
filterAlias = true
): Promise<string[] | null> {
const res = await fetch(
`https://api.revenuecat.com/v2/projects/${this.projectId}/customers/${customerId}/aliases`,
{
@@ -204,9 +246,12 @@ export class RevenueCatService {
const customerParsed = zRcV2RawCustomerAliasEnvelope.safeParse(json);
if (customerParsed.success) {
return customerParsed.data.items
.map(alias => alias.id)
.filter(id => !id.startsWith('$RCAnonymousID:'));
const customer = customerParsed.data.items.map(alias => alias.id);
if (filterAlias) {
return customer.filter(id => !id.startsWith('$RCAnonymousID:'));
} else {
return customer;
}
}
this.logger.error(
`RevenueCat customer ${customerId} parse failed: ${JSON.stringify(

Some files were not shown because too many files have changed in this diff Show More