Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
6195570
Update
lakeeast Jun 10, 2025
8af3bbd
Added floppy save icon
lakeeast Jun 11, 2025
e82bd18
Update checkin commands
lakeeast Jun 11, 2025
432bdc9
Save is working
lakeeast Jun 12, 2025
53b5bf0
Load implemented
lakeeast Jun 12, 2025
f243b9d
Added build options to handle large package
lakeeast Jun 12, 2025
6776ba3
Added dicom insert
lakeeast Jun 13, 2025
3d8c67c
Added DICOM menu
lakeeast Jun 14, 2025
86bb12c
Show DICOM
lakeeast Jun 14, 2025
48a7b23
Close to work with attachment adjustment
lakeeast Jun 14, 2025
1dd571d
Avoid crash when add or remove files
lakeeast Jun 14, 2025
12cecbd
Sync added
lakeeast Jun 14, 2025
087ff91
Save DICOM separately from document
lakeeast Jun 18, 2025
4cf61d3
Load dicom on demand
lakeeast Jun 20, 2025
3d73dce
Not sure
lakeeast Jun 20, 2025
c72ec93
Update
lakeeast Jun 20, 2025
5cec192
Support do/undo
lakeeast Jun 22, 2025
1fa52b3
Revert attachment-block to support PDF view again
lakeeast Jun 24, 2025
4610d2f
Revert to original DICOM that requires a refresh click
lakeeast Jun 24, 2025
ce854e3
Embed view is at least working. DICOM View is not
lakeeast Jun 26, 2025
f6ba22d
Fix DICOM
lakeeast Jun 26, 2025
8c6cbd2
Hide warning icon
lakeeast Jun 28, 2025
4dac9e6
Fix viewing attachment for both cloud and local
lakeeast Jul 1, 2025
04e1bcf
Reset default content
lakeeast Jul 1, 2025
103a16a
ROUT-5 Remove unused dicom model code
lakeeast Jul 8, 2025
af70a83
ROUT-5 Added qt-sdk dependency
lakeeast Jul 8, 2025
f57dcc6
ROUT-5 Show DICOM with study manager
lakeeast Jul 8, 2025
2b1cf5e
ROUT-5 Saving DICOM added
lakeeast Jul 8, 2025
7df7e00
ROUT-5 Saving DICOM is working, but seems to be overwriting each other
lakeeast Jul 8, 2025
dc26af7
ROUT-5 Save multiple DICOM files without name collision
lakeeast Jul 8, 2025
01c4054
ROUT-5 Working now by decoupling decoder and viewer
lakeeast Jul 10, 2025
e7ea349
ROUT-6 Use local iframe to avoid zone.js issue without setting a sepa…
lakeeast Jul 11, 2025
c954503
ROUT-5 Updated history
lakeeast Jul 11, 2025
cb0b7d9
ROUT-5 Remove sandbox option
lakeeast Jul 14, 2025
99562a1
ROUT-5 Change fonts to local version
lakeeast Jul 14, 2025
0d1090c
ROUT-5 allow weasis
lakeeast Jul 14, 2025
7db47bb
ROUT-5 Added size display when saving
lakeeast Jul 16, 2025
b9499cd
ROUT-5 Progress
lakeeast Jul 16, 2025
1d71e38
ROUT-5 Handle images
lakeeast Jul 17, 2025
7c8def9
ROUT-5 DICOM missed
lakeeast Jul 17, 2025
c0cd315
ROUT-5 Fix duplicated source ID issue by generating unique zip
lakeeast Jul 17, 2025
e7d1000
ROUT-5 Fix history
lakeeast Jul 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CONTRIBUTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Check in
eval `ssh-agent -s`
ssh-add --apple-use-keychain ~/.ssh/id_ed25519

## How to prepare locally?
yarn install

## How to run locally?
yarn dev

## How to build locally?
NODE_OPTIONS="--max-old-space-size=4096" yarn workspace @blocksuite/playground build
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"ci:version:canary": "changeset version --snapshot canary && yarn install --no-frozen-lockfile",
"ci:publish:canary": "yarn build:packages && yarn workspaces foreach -Ap --no-private npm publish --access public --tag canary",
"postinstall": "husky",
"changeset": "changeset && node scripts/changelog.mjs"
"changeset": "changeset && node scripts/changelog.mjs",
"deploy": "aws s3 sync packages/playground/dist s3://block.docnosys.com --delete --profile Chen"
},
"lint-staged": {
"!packages/**/*": [
Expand Down
3 changes: 3 additions & 0 deletions packages/affine/blocks/attachment/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.15",
"file-type": "^21.0.0",
"jszip": "^3.10.1",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"quantant-storage": "^0.0.41",
"rxjs": "^7.8.1",
"uuid": "^11.0.3",
"zod": "^3.23.8"
},
"exports": {
Expand Down
156 changes: 48 additions & 108 deletions packages/affine/blocks/attachment/src/attachment-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import {
AttachmentIcon,
ResetIcon,
UpgradeIcon,
WarningIcon,
} from '@blocksuite/icons/lit';
import { BlockSelection } from '@blocksuite/std';
import { nanoid, Slice } from '@blocksuite/store';
Expand Down Expand Up @@ -120,22 +119,30 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
window.open(blobUrl, '_blank');
};

// Refreshes data.
refreshData = () => {
refreshData(this).catch(console.error);
};

private readonly _refreshKey$ = signal<string | null>(null);

// Refreshes the embed component.
reload = () => {
if (this.model.props.embed) {
this._refreshKey$.value = nanoid();
return;
}
reload = () => {
const isEmbedded = this.model.props.embed;
const hasBlobUrl = !!this.resourceController.blobUrl$.value;

this.refreshData();
};
if (isEmbedded && hasBlobUrl) {
// For embedded attachments with a loaded blob (likely local or cached), update refreshKey
this._refreshKey$.value = nanoid();
console.log('Embedded reload with blobUrl, updated refreshKey:', this._refreshKey$.value);
return;
}

// For non-embedded or cloud attachments (no blobUrl), perform full refresh
this.resourceController.updateState({ downloading: true });
this.refreshData();
this.resourceController.updateState({ downloading: false, state: 'none' });
this._refreshKey$.value = nanoid();
console.log('Full reload, updated refreshKey:', this._refreshKey$.value);
};

private _selectBlock() {
const selectionManager = this.host.selection;
Expand All @@ -146,7 +153,6 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
}

private readonly _trackCitationDeleteEvent = () => {
// Check citation delete event
this._disposables.add(
this.std.store.slots.blockUpdated
.pipe(
Expand Down Expand Up @@ -201,12 +207,10 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
}

override firstUpdated() {
// lazy bindings
this.disposables.addFromEvent(this, 'click', this.onClick);
}

protected onClick(event: MouseEvent) {
// the peek view need handle shift + click
if (event.defaultPrevented) return;

event.stopPropagation();
Expand All @@ -230,67 +234,29 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
event.stopPropagation();
onOverFileSize?.();

{
const mode =
this.std.get(DocModeProvider).getEditorMode() ?? 'page';
const segment = mode === 'page' ? 'doc' : 'whiteboard';
this.std
.getOptional(TelemetryProvider)
?.track('AttachmentUpgradedEvent', {
segment,
page: `${segment} editor`,
module: 'attachment',
control: 'upgrade',
category: 'card',
type: this.model.props.name.split('.').pop() ?? '',
});
}
}}
>
${UpgradeIcon()} Upgrade
</button>
`
);
};

protected renderNormalButton = (needUpload: boolean) => {
const label = needUpload ? 'retry' : 'reload';
const run = async () => {
if (needUpload) {
await this.resourceController.upload();
return;
}

this.refreshData();
};

return html`
<button
class="affine-attachment-content-button"
@click=${(event: MouseEvent) => {
event.stopPropagation();
run().catch(console.error);

{
const mode =
this.std.get(DocModeProvider).getEditorMode() ?? 'page';
const segment = mode === 'page' ? 'doc' : 'whiteboard';
this.std
.getOptional(TelemetryProvider)
?.track('AttachmentReloadedEvent', {
?.track('AttachmentUpgradedEvent', {
segment,
page: `${segment} editor`,
module: 'attachment',
control: label,
control: 'upgrade',
category: 'card',
type: this.filetype,
type: this.model.props.name.split('.').pop() ?? '',
});
}
}}
>
${ResetIcon()} ${label}
</button>
`;
}}
>
${UpgradeIcon()} Upgrade
</button>
`
);
};

protected renderNormalButton = (needUpload: boolean) => {
return null;
};

protected renderWithHorizontal(
Expand All @@ -313,16 +279,6 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
${title}
</div>
</div>

<div class="affine-attachment-content-description">
<div class="affine-attachment-content-info truncate">
${description}
</div>
${choose(state, [
['error', () => this.renderNormalButton(needUpload)],
['error:oversize', this.renderUpgradeButton],
])}
</div>
</div>

<div class="affine-attachment-banner">${kind}</div>
Expand Down Expand Up @@ -356,13 +312,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
</div>
</div>

<div class="affine-attachment-banner">
${kind}
${choose(state, [
['error', () => this.renderNormalButton(needUpload)],
['error:oversize', this.renderUpgradeButton],
])}
</div>
<div class="affine-attachment-banner">${kind}</div>
</div>
`;
}
Expand All @@ -374,7 +324,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment

const resolvedState = this.resourceController.resolveStateWith({
loadingIcon: LoadingIcon(),
errorIcon: WarningIcon(),
errorIcon: null, // Suppress warning symbol
icon: AttachmentIcon(),
title: name,
description: formatSize(size),
Expand All @@ -391,7 +341,6 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
'affine-attachment-card': true,
[cardStyle]: true,
loading: resolvedState.loading,
error: resolvedState.error,
};

return when(
Expand All @@ -403,40 +352,30 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment

protected renderEmbedView = () => {
const { model, blobUrl } = this;
if (!model.props.embed || !blobUrl) return null;
if (!model.props.embed || !blobUrl) {
console.log('renderEmbedView: Skipping due to missing embed or blobUrl', {
embed: model.props.embed,
blobUrl,
name: model.props.name,
});
return null;
}

const { std, _maxFileSize } = this;
const provider = std.get(AttachmentEmbedProvider);

const render = provider.getRender(model, _maxFileSize);
if (!render) return null;

const enabled = provider.shouldShowStatus(model);
if (!render) {
console.log('renderEmbedView: No render function available', {
name: model.props.name,
});
return null;
}

return html`
<div class="affine-attachment-embed-container">
${guard([this._refreshKey$.value], () => render(model, blobUrl))}
</div>
${when(enabled, () => {
const resolvedState = this.resolvedState$.value;
if (resolvedState.state !== 'error') return null;
// It should be an error messge.
const message = resolvedState.description;
if (!message) return null;

const needUpload = resolvedState.needUpload;
const action = () =>
needUpload ? this.resourceController.upload() : this.reload();

return html`
<affine-resource-status
class="affine-attachment-embed-status"
.message=${message}
.needUpload=${needUpload}
.action=${action}
></affine-resource-status>
`;
})}
`;
};

Expand All @@ -453,6 +392,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
};

override renderBlock() {
console.log('renderBlock called, flavour:', this.model.flavour, 'state:', this.resolvedState$.value.state);
return html`
<div
class=${classMap({
Expand All @@ -479,4 +419,4 @@ declare global {
interface HTMLElementTagNameMap {
'affine-attachment': AttachmentBlockComponent;
}
}
}
Loading