snapshot of https://github.com/toeverything/AFFiNE.git (canary, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (1) (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 4s
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 / 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 / Analyze (javascript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, affine) (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 / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
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 / 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 / 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 @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 / 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 (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (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 (5, 5) (push) Has been skipped
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 / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (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 (x86_64-apple-darwin) (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

This commit is contained in:
upstream-mirror
2026-09-16 00:31:07 +05:00
commit 50f788e799
7907 changed files with 988785 additions and 0 deletions

View File

@@ -0,0 +1,277 @@
import type {
LinkPreviewProvider,
LinkPreviewResult,
} from '@blocksuite/affine-shared/services';
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { ToggleDownIcon, ToggleRightIcon } from '@blocksuite/icons/lit';
import { flip, offset, shift } from '@floating-ui/dom';
import { css, html, LitElement, nothing, type PropertyValues } from 'lit';
import { property, state } from 'lit/decorators.js';
import { createLitPortal } from '../portal';
export class LinkPreviewDetails extends LitElement {
static override styles = css`
:host {
display: block;
color: ${unsafeCSSVarV2('text/primary')};
font: 13px/20px var(--affine-font-family);
}
:host([floating]) {
position: absolute;
right: 8px;
bottom: 8px;
}
:host([floating]) button {
background: ${unsafeCSSVarV2('layer/background/primary')};
box-shadow: 0 0 0 1px ${unsafeCSSVarV2('layer/background/tertiary')};
}
.content.floating {
margin: 0;
width: min(360px, calc(100vw - 32px));
box-sizing: border-box;
box-shadow: var(--affine-menu-shadow);
}
button {
display: flex;
align-items: center;
gap: 4px;
border: 0;
border-radius: 4px;
padding: 4px 8px;
background: transparent;
color: ${unsafeCSSVarV2('text/secondary')};
font: inherit;
cursor: pointer;
}
button:hover {
background: var(--affine-hover-color);
}
button:focus-visible {
outline: 2px solid var(--affine-primary-color);
}
.content {
color: inherit;
margin-top: 4px;
padding: 12px;
border: 1px solid ${unsafeCSSVarV2('layer/background/tertiary')};
border-radius: 8px;
background: ${unsafeCSSVarV2('layer/background/primary')};
max-height: 320px;
overflow: auto;
user-select: text;
overflow-wrap: anywhere;
}
.meta,
.status,
time {
color: ${unsafeCSSVarV2('text/secondary')};
}
.segment {
margin-top: 8px;
}
.segment-text,
.description {
white-space: pre-wrap;
}
h4 {
margin: 12px 0 4px;
font: inherit;
font-weight: 600;
}
time {
margin-right: 8px;
font-variant-numeric: tabular-nums;
}
`;
@property({ attribute: false }) accessor url = '';
@property({ type: Boolean, reflect: true }) accessor floating = false;
@property({ attribute: false }) accessor provider!: LinkPreviewProvider;
@state() private accessor open = false;
@state() private accessor loading = false;
@state() private accessor value: LinkPreviewResult | undefined;
private controller?: AbortController;
private popupController?: AbortController;
private updatePopup?: () => void;
protected override willUpdate(changed: PropertyValues) {
if (changed.has('url') || changed.has('provider')) {
this.controller?.abort();
this.open = false;
this.loading = false;
this.value = undefined;
}
}
override disconnectedCallback() {
super.disconnectedCallback();
this.popupController?.abort();
this.controller?.abort();
this.open = false;
this.loading = false;
}
protected override updated() {
if (!this.floating || !this.open) {
this.popupController?.abort();
return;
}
if (this.popupController) {
this.updatePopup?.();
return;
}
const button = this.shadowRoot?.querySelector('button');
if (!button) return;
const controller = new AbortController();
this.popupController = controller;
controller.signal.addEventListener('abort', () => {
this.popupController = undefined;
this.updatePopup = undefined;
this.open = false;
});
document.addEventListener(
'keydown',
event => {
if (event.key !== 'Escape') return;
event.stopPropagation();
controller.abort();
button.focus();
},
{ signal: controller.signal }
);
const { portal } = createLitPortal({
template: ({ updatePortal }) => {
this.updatePopup = updatePortal;
return html`<style>
${LinkPreviewDetails.styles.cssText}</style
>${this.renderContent()}`;
},
computePosition: {
referenceElement: button,
placement: 'bottom-end',
middleware: [offset(8), flip(), shift({ padding: 16 })],
autoUpdate: { animationFrame: true },
},
abortController: controller,
positionStrategy: 'fixed',
closeOnClickAway: true,
});
portal.shadowRoot?.querySelector<HTMLElement>('.content')?.focus();
}
private async toggle() {
this.open = !this.open;
if (!this.open || this.loading || this.value) return;
const controller = new AbortController();
this.controller = controller;
this.loading = true;
try {
const value = await this.provider.query(this.url, controller.signal, [
'transcript',
]);
if (!controller.signal.aborted) this.value = value;
} catch {
if (!controller.signal.aborted) this.value = {};
} finally {
if (this.controller === controller) this.loading = false;
}
}
override render() {
return html`<div
contenteditable="false"
@click=${(event: Event) => event.stopPropagation()}
@dblclick=${(event: Event) => event.stopPropagation()}
@pointerdown=${(event: Event) => event.stopPropagation()}
>
<button
type="button"
aria-expanded=${this.open}
aria-controls=${this.floating ? nothing : 'details'}
aria-haspopup=${this.floating ? 'dialog' : nothing}
@click=${this.toggle}
>
${(this.open ? ToggleDownIcon : ToggleRightIcon)({ width: '16px', height: '16px' })}
Details
</button>
${this.open && !this.floating ? this.renderContent() : nothing}
</div>`;
}
private renderContent() {
const value = this.value;
const transcript = value?.transcript;
const metadata = [
value?.author?.name,
value?.publishedAt?.split('T')[0],
value?.durationSeconds === undefined
? undefined
: timestamp(value.durationSeconds),
].filter(Boolean);
let chapterIndex = 0;
const chapters = transcript?.chapters ?? [];
return html`<div
class="content ${this.floating ? 'floating' : ''}"
id="details"
contenteditable="false"
aria-busy=${this.loading}
role=${this.floating ? 'dialog' : 'region'}
aria-label="Link details"
tabindex="-1"
@click=${(event: Event) => event.stopPropagation()}
@dblclick=${(event: Event) => event.stopPropagation()}
@pointerdown=${(event: Event) => event.stopPropagation()}
@wheel=${(event: Event) => event.stopPropagation()}
>
${
this.loading
? html`<span class="status" role="status">Loading details…</span>`
: html`
${metadata.length ? html`<div class="meta">${metadata.join(' · ')}</div>` : nothing}
${value?.description ? html`<p class="description">${value.description}</p>` : nothing}
${
transcript?.segments.length
? html`
<h4>Transcript</h4>
${transcript.segments.map(segment => {
const headings = [];
while (
chapterIndex < chapters.length &&
chapters[chapterIndex].startSeconds <=
(segment.startSeconds ?? 0)
) {
headings.push(
html`<h4>${chapters[chapterIndex++].title}</h4>`
);
}
return html`${headings}
<div class="segment">
${segment.startSeconds === undefined ? nothing : html`<time>${timestamp(segment.startSeconds)}</time>`}${segment.speaker ? html`<strong>${segment.speaker}: </strong>` : nothing}<span
class="segment-text"
>${segment.text}</span
>
</div>`;
})}
${transcript.truncated ? html`<p class="status">Transcript truncated by the preview service.</p>` : nothing}
`
: html`<div class="status">No transcript available.</div>`
}
`
}
</div>`;
}
}
function timestamp(seconds: number) {
const minutes = Math.floor(seconds / 60);
return `${minutes}:${Math.floor(seconds % 60)
.toString()
.padStart(2, '0')}`;
}
declare global {
interface HTMLElementTagNameMap {
'affine-link-preview-details': LinkPreviewDetails;
}
}