snapshot of https://github.com/toeverything/AFFiNE.git (canary, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Build & Test / Analyze (javascript, affine) (push) Failing after 4s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 4s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 3s
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 4s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 3s
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Release / Canary Gate (push) Has been skipped
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Copilot Test Automatically / Setup Test (push) Failing after 1s
Release / Prepare (push) Failing after 2s
Release / Release Cloud (push) Has been skipped
Release / Release Desktop (push) Has been skipped
Release / Release Docker Image (push) Has been skipped
Release / Release Mobile (push) Has been skipped
Some checks failed
Build & Test / Analyze (javascript, affine) (push) Failing after 4s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 4s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 3s
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 4s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 3s
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Release / Canary Gate (push) Has been skipped
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Copilot Test Automatically / Setup Test (push) Failing after 1s
Release / Prepare (push) Failing after 2s
Release / Release Cloud (push) Has been skipped
Release / Release Desktop (push) Has been skipped
Release / Release Docker Image (push) Has been skipped
Release / Release Mobile (push) Has been skipped
This commit is contained in:
@@ -0,0 +1,324 @@
|
||||
import { getAttachmentFileIcon } from '@blocksuite/affine/components/icons';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit';
|
||||
import type { AttachmentBlockModel } from '@blocksuite/affine-model';
|
||||
import { formatSize } from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
ArrowDownBigIcon,
|
||||
ArrowUpBigIcon,
|
||||
CloseIcon,
|
||||
} from '@blocksuite/icons/lit';
|
||||
import { signal } from '@preact/signals-core';
|
||||
import { css, html, LitElement, type TemplateResult } from 'lit';
|
||||
import { customElement, query } from 'lit/decorators.js';
|
||||
|
||||
import type { DocInfo, MessageData, MessageDataType } from './pdf/types.js';
|
||||
import { MessageOp, RenderKind, State } from './pdf/types.js';
|
||||
|
||||
const DPI = window.devicePixelRatio;
|
||||
|
||||
type FileInfo = {
|
||||
name: string;
|
||||
size: string | null;
|
||||
isPDF: boolean;
|
||||
icon: TemplateResult;
|
||||
};
|
||||
|
||||
@customElement('attachment-viewer-panel')
|
||||
export class AttachmentViewerPanel extends SignalWatcher(
|
||||
WithDisposable(LitElement)
|
||||
) {
|
||||
static override styles = css`
|
||||
:host {
|
||||
dialog {
|
||||
padding: 0;
|
||||
top: 50px;
|
||||
border: 1px solid var(--affine-border-color);
|
||||
border-radius: 8px;
|
||||
background: var(--affine-v2-dialog-background-primary);
|
||||
box-shadow: var(--affine-overlay-shadow);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 700px;
|
||||
height: 900px;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
|
||||
& > .close {
|
||||
user-select: none;
|
||||
outline: none;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
header,
|
||||
footer {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: var(--affine-text-secondary-color);
|
||||
}
|
||||
|
||||
h5 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
margin: 0;
|
||||
|
||||
.file-icon svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
overflow-y: auto;
|
||||
|
||||
.page {
|
||||
width: calc(100% - 40px);
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.error {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.controls {
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
right: 20px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
readonly #cursor = signal<number>(0);
|
||||
|
||||
readonly #docInfo = signal<DocInfo | null>(null);
|
||||
|
||||
readonly #fileInfo = signal<FileInfo | null>(null);
|
||||
|
||||
readonly #state = signal<State>(State.Connecting);
|
||||
|
||||
#worker: Worker | null = null;
|
||||
|
||||
clear = () => {
|
||||
this.#dialog.close();
|
||||
|
||||
this.#state.value = State.IDLE;
|
||||
this.#worker?.terminate();
|
||||
this.#worker = null;
|
||||
|
||||
this.#fileInfo.value = null;
|
||||
this.#docInfo.value = null;
|
||||
this.#cursor.value = 0;
|
||||
|
||||
const canvas = this.#page;
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
};
|
||||
|
||||
goto(at: number) {
|
||||
this.#cursor.value = at;
|
||||
this.post(MessageOp.Render, {
|
||||
index: at,
|
||||
scale: 1 * DPI,
|
||||
kind: RenderKind.Page,
|
||||
});
|
||||
}
|
||||
|
||||
open(model: AttachmentBlockModel) {
|
||||
this.#dialog.showModal();
|
||||
|
||||
const { name, size } = model.props;
|
||||
|
||||
const fileType = name.split('.').pop() ?? '';
|
||||
const icon = getAttachmentFileIcon(fileType);
|
||||
const isPDF = fileType === 'pdf';
|
||||
|
||||
this.#fileInfo.value = {
|
||||
name,
|
||||
icon,
|
||||
isPDF,
|
||||
size: formatSize(size),
|
||||
};
|
||||
|
||||
if (!isPDF) return;
|
||||
if (!model.props.sourceId) return;
|
||||
if (this.#worker) return;
|
||||
|
||||
const process = async ({ data }: MessageEvent<MessageData>) => {
|
||||
const { type } = data;
|
||||
|
||||
switch (type) {
|
||||
case MessageOp.Init: {
|
||||
console.debug('connecting');
|
||||
this.#state.value = State.Connecting;
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageOp.Inited: {
|
||||
console.debug('connected');
|
||||
this.#state.value = State.Connected;
|
||||
|
||||
const blob = await model.store.blobSync.get(model.props.sourceId!);
|
||||
|
||||
if (!blob) return;
|
||||
const buffer = await blob.arrayBuffer();
|
||||
|
||||
this.post(MessageOp.Open, buffer, [buffer]);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageOp.Opened: {
|
||||
const info = data[type];
|
||||
this.#cursor.value = 0;
|
||||
this.#docInfo.value = info;
|
||||
this.#state.value = State.Opened;
|
||||
this.post(MessageOp.Render, {
|
||||
index: 0,
|
||||
scale: 1 * DPI,
|
||||
kind: RenderKind.Page,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageOp.Rendered: {
|
||||
const { index, kind, imageData } = data[type];
|
||||
|
||||
if (index !== this.#cursor.value) return;
|
||||
|
||||
const canvas = this.#page;
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
console.debug('render page', index, kind);
|
||||
canvas.width = imageData.width;
|
||||
canvas.height = imageData.height;
|
||||
|
||||
ctx.clearRect(0, 0, imageData.width, imageData.height);
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.#worker = new Worker(new URL('./pdf/worker.ts', import.meta.url), {
|
||||
type: 'module',
|
||||
});
|
||||
|
||||
this.#worker.addEventListener('message', event => {
|
||||
process(event).catch(console.error);
|
||||
});
|
||||
}
|
||||
|
||||
post<T extends MessageOp>(
|
||||
type: T,
|
||||
data?: MessageDataType[T],
|
||||
transfers?: Transferable[]
|
||||
) {
|
||||
if (!this.#worker) return;
|
||||
|
||||
const message = { type, [type]: data };
|
||||
if (transfers?.length) {
|
||||
this.#worker?.postMessage(message, transfers);
|
||||
return;
|
||||
}
|
||||
|
||||
this.#worker?.postMessage(message);
|
||||
}
|
||||
|
||||
override render() {
|
||||
const fileInfo = this.#fileInfo.value;
|
||||
const isPDF = fileInfo?.isPDF ?? false;
|
||||
const docInfo = this.#docInfo.value;
|
||||
const cursor = this.#cursor.value;
|
||||
const total = docInfo ? docInfo.total : 0;
|
||||
const width = docInfo ? docInfo.width : 0;
|
||||
const height = docInfo ? docInfo.height : 0;
|
||||
const isEmpty = total === 0;
|
||||
const print = (n: number) => (isEmpty ? '-' : n);
|
||||
|
||||
return html`
|
||||
<dialog>
|
||||
<div class="dialog">
|
||||
<header>
|
||||
<h5>
|
||||
<span>${fileInfo?.name}</span>
|
||||
<span>${fileInfo?.size}</span>
|
||||
<span class="file-icon">${fileInfo?.icon}</span>
|
||||
</h5>
|
||||
</header>
|
||||
<main class="body">
|
||||
${
|
||||
isPDF
|
||||
? html`<canvas class="page"></canvas>`
|
||||
: html`<p class="error">This file format is not supported.</p>`
|
||||
}
|
||||
<div class="controls">
|
||||
<icon-button
|
||||
.disabled=${isEmpty || cursor === 0}
|
||||
@click=${() => this.goto(cursor - 1)}
|
||||
>${ArrowUpBigIcon()}</icon-button
|
||||
>
|
||||
<icon-button
|
||||
.disabled=${isEmpty || cursor + 1 === total}
|
||||
@click=${() => this.goto(cursor + 1)}
|
||||
>${ArrowDownBigIcon()}</icon-button
|
||||
>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
<div>
|
||||
<span>${print(width)}</span>
|
||||
x
|
||||
<span>${print(height)}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>${print(cursor + 1)}</span>
|
||||
/
|
||||
<span>${print(total)}</span>
|
||||
</div>
|
||||
</footer>
|
||||
<icon-button class="close" @click=${this.clear}
|
||||
>${CloseIcon()}</icon-button
|
||||
>
|
||||
</div>
|
||||
</dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
@query('dialog')
|
||||
accessor #dialog!: HTMLDialogElement;
|
||||
|
||||
@query('.page')
|
||||
accessor #page: HTMLCanvasElement | null = null;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'attachment-viewer-panel': AttachmentViewerPanel;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user