@formicoidea/labre-core 0.46.1 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_pkgs/affine-block-bookmark/bookmark-block.js +5 -2
- package/dist/_pkgs/affine-block-data-view/columns/index.d.ts +1 -1
- package/dist/_pkgs/affine-block-database/data-source.d.ts +1 -1
- package/dist/_pkgs/affine-block-database/properties/converts.d.ts +4 -4
- package/dist/_pkgs/affine-block-database/properties/index.d.ts +1 -1
- package/dist/_pkgs/affine-block-database/properties/link/cell-renderer.js +2 -2
- package/dist/_pkgs/affine-block-embed/embed-figma-block/embed-figma-block.js +3 -1
- package/dist/_pkgs/affine-block-embed/embed-github-block/embed-github-block.js +3 -1
- package/dist/_pkgs/affine-block-embed/embed-loom-block/embed-loom-block.js +3 -1
- package/dist/_pkgs/affine-block-embed/embed-youtube-block/embed-youtube-block.js +3 -1
- package/dist/_pkgs/affine-block-frame/commands.js +41 -1
- package/dist/_pkgs/affine-block-frame/frame-manager.d.ts +1 -1
- package/dist/_pkgs/affine-block-frame/view.js +7 -2
- package/dist/_pkgs/affine-block-note/components/edgeless-page-block-title.d.ts +7 -0
- package/dist/_pkgs/affine-block-note/components/edgeless-page-block-title.js +15 -1
- package/dist/_pkgs/affine-block-surface/extensions/canvas-grid/commands.js +20 -6
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/commands.d.ts +2 -0
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/commands.js +79 -0
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.d.ts +18 -1
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.js +53 -30
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/index.d.ts +1 -0
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/index.js +1 -0
- package/dist/_pkgs/affine-block-surface/extensions/reading-toolbar.js +12 -12
- package/dist/_pkgs/affine-block-surface/view.js +11 -2
- package/dist/_pkgs/affine-components/highlight-dropdown-menu/highlight-duotone-icon.js +8 -8
- package/dist/_pkgs/affine-components/highlight-dropdown-menu/text-duotone-icon.js +11 -11
- package/dist/_pkgs/affine-components/link-preview/link.js +3 -3
- package/dist/_pkgs/affine-fragment-doc-title/doc-title.d.ts +8 -0
- package/dist/_pkgs/affine-fragment-doc-title/doc-title.js +65 -4
- package/dist/_pkgs/affine-fragment-doc-title/effects.d.ts +2 -0
- package/dist/_pkgs/affine-fragment-doc-title/effects.js +2 -0
- package/dist/_pkgs/affine-fragment-doc-title/index.d.ts +2 -0
- package/dist/_pkgs/affine-fragment-doc-title/index.js +2 -0
- package/dist/_pkgs/affine-fragment-doc-title/title-band/format.d.ts +69 -0
- package/dist/_pkgs/affine-fragment-doc-title/title-band/format.js +182 -0
- package/dist/_pkgs/affine-fragment-doc-title/title-band/title-band.d.ts +62 -0
- package/dist/_pkgs/affine-fragment-doc-title/title-band/title-band.js +649 -0
- package/dist/_pkgs/affine-fragment-doc-title/translations.d.ts +21 -0
- package/dist/_pkgs/affine-fragment-doc-title/translations.js +58 -0
- package/dist/_pkgs/affine-inline-footnote/footnote-node/footnote-node.js +3 -0
- package/dist/_pkgs/affine-inline-link/adapters/markdown/markdown-inline.js +5 -0
- package/dist/_pkgs/affine-inline-link/link-node/affine-link.js +6 -3
- package/dist/_pkgs/affine-shared/services/index.d.ts +2 -0
- package/dist/_pkgs/affine-shared/services/index.js +2 -0
- package/dist/_pkgs/affine-shared/services/properties-panel-service.d.ts +50 -0
- package/dist/_pkgs/affine-shared/services/properties-panel-service.js +17 -0
- package/dist/_pkgs/affine-shared/services/title-band-service.d.ts +124 -0
- package/dist/_pkgs/affine-shared/services/title-band-service.js +17 -0
- package/dist/_pkgs/affine-shared/utils/url.d.ts +15 -0
- package/dist/_pkgs/affine-shared/utils/url.js +30 -0
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/edgeless-element-link.js +2 -2
- package/dist/_pkgs/affine-widget-edgeless-zoom-toolbar/index.js +26 -26
- package/dist/_pkgs/affine-widget-keyboard-toolbar/icons.js +98 -98
- package/dist/_pkgs/affine-widget-page-dragging-area/index.js +10 -10
- package/dist/_pkgs/affine-widget-viewport-overlay/index.js +13 -13
- package/dist/_pkgs/data-view/core/utils/menu-title.js +17 -17
- package/dist/_pkgs/data-view/property-presets/converts.d.ts +2 -2
- package/dist/_pkgs/data-view/property-presets/index.d.ts +1 -1
- package/dist/_pkgs/data-view/property-presets/number/cell-renderer.d.ts +1 -1
- package/dist/_pkgs/data-view/property-presets/number/define.d.ts +1 -1
- package/dist/_pkgs/data-view/property-presets/number/types.d.ts +2 -2
- package/dist/_pkgs/data-view/property-presets/pure-index.d.ts +2 -1
- package/dist/_pkgs/data-view/property-presets/pure-index.js +4 -0
- package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/group-title.js +18 -18
- package/dist/_pkgs/std/extension/command-registry.d.ts +50 -1
- package/dist/_pkgs/std/extension/command-registry.js +26 -7
- package/dist/commands.js +2 -1
- package/dist/host-panels.d.ts +83 -2
- package/dist/host-panels.js +82 -1
- package/package.json +1 -1
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/file-exporter.d.ts +0 -26
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/file-exporter.js +0 -73
|
@@ -11,7 +11,65 @@ export const DOC_TITLE_PLACEHOLDER = [
|
|
|
11
11
|
'com.labre.doc-title.placeholder',
|
|
12
12
|
'Title',
|
|
13
13
|
];
|
|
14
|
+
/* ── The title band (ADR 0035 §11), `./title-band/title-band.ts` ───────── */
|
|
15
|
+
/** The band's accessible name: the group of property chips. */
|
|
16
|
+
export const TITLE_BAND_LABEL = [
|
|
17
|
+
'com.labre.title-band.label',
|
|
18
|
+
'Page properties',
|
|
19
|
+
];
|
|
20
|
+
/** What an empty chip says after the property's name (REQ-PR-1). */
|
|
21
|
+
export const TITLE_BAND_EMPTY = [
|
|
22
|
+
'com.labre.title-band.empty',
|
|
23
|
+
'Not set',
|
|
24
|
+
];
|
|
25
|
+
/** The "+" (REQ-PR-4); what it does is the host's. */
|
|
26
|
+
export const TITLE_BAND_ADD = [
|
|
27
|
+
'com.labre.title-band.add',
|
|
28
|
+
'Add a property',
|
|
29
|
+
];
|
|
30
|
+
/** The button beside the "+" that opens the host's properties panel. */
|
|
31
|
+
export const TITLE_BAND_OPEN_PANEL = [
|
|
32
|
+
'com.labre.title-band.open-panel',
|
|
33
|
+
'Open the properties panel',
|
|
34
|
+
];
|
|
35
|
+
/** Folds the chips away. */
|
|
36
|
+
export const TITLE_BAND_COLLAPSE = [
|
|
37
|
+
'com.labre.title-band.collapse',
|
|
38
|
+
'Hide properties',
|
|
39
|
+
];
|
|
40
|
+
/** The folded band's only button; `count` is the number of chips. */
|
|
41
|
+
export const TITLE_BAND_EXPAND = [
|
|
42
|
+
'com.labre.title-band.expand',
|
|
43
|
+
'Properties ({{count}})',
|
|
44
|
+
];
|
|
45
|
+
/** The accessible name of the skeleton drawn while the sheet loads. */
|
|
46
|
+
export const TITLE_BAND_LOADING = [
|
|
47
|
+
'com.labre.title-band.loading',
|
|
48
|
+
'Loading properties',
|
|
49
|
+
];
|
|
50
|
+
/** Clears a select or a date from its editor. */
|
|
51
|
+
export const TITLE_BAND_CLEAR = [
|
|
52
|
+
'com.labre.title-band.clear',
|
|
53
|
+
'Clear value',
|
|
54
|
+
];
|
|
55
|
+
/**
|
|
56
|
+
* The note header's row of child pages the document holds but does not
|
|
57
|
+
* display (ADR 0035 §9d, canvas view only).
|
|
58
|
+
*/
|
|
59
|
+
export const TITLE_BAND_NOT_SHOWN = [
|
|
60
|
+
'com.labre.title-band.not-shown',
|
|
61
|
+
'Not shown in this document',
|
|
62
|
+
];
|
|
14
63
|
/** Every wording this package declares, in the order it renders them. */
|
|
15
64
|
export const DOC_TITLE_WORDINGS = [
|
|
16
65
|
DOC_TITLE_PLACEHOLDER,
|
|
66
|
+
TITLE_BAND_ADD,
|
|
67
|
+
TITLE_BAND_CLEAR,
|
|
68
|
+
TITLE_BAND_COLLAPSE,
|
|
69
|
+
TITLE_BAND_EMPTY,
|
|
70
|
+
TITLE_BAND_EXPAND,
|
|
71
|
+
TITLE_BAND_LABEL,
|
|
72
|
+
TITLE_BAND_LOADING,
|
|
73
|
+
TITLE_BAND_NOT_SHOWN,
|
|
74
|
+
TITLE_BAND_OPEN_PANEL,
|
|
17
75
|
];
|
|
@@ -36,6 +36,7 @@ import { HoverController } from '../../affine-components/hover/index.js';
|
|
|
36
36
|
import { PeekViewProvider } from '../../affine-components/peek/index.js';
|
|
37
37
|
import { CitationProvider } from '../../affine-shared/services/index.js';
|
|
38
38
|
import { unsafeCSSVarV2 } from '../../affine-shared/theme/index.js';
|
|
39
|
+
import { isSafeLinkUrl } from '../../affine-shared/utils/index.js';
|
|
39
40
|
import { WithDisposable } from '../../global/lit/index.js';
|
|
40
41
|
import { BlockSelection, ShadowlessElement, TextSelection, } from '../../std/index.js';
|
|
41
42
|
import { INLINE_ROOT_ATTR, ZERO_WIDTH_FOR_EMBED_NODE, ZERO_WIDTH_FOR_EMPTY_LINE, } from '../../std/inline/index.js';
|
|
@@ -203,6 +204,8 @@ let AffineFootnoteNode = (() => {
|
|
|
203
204
|
.catch(console.error);
|
|
204
205
|
};
|
|
205
206
|
this._handleUrlReference = (url) => {
|
|
207
|
+
if (!isSafeLinkUrl(url))
|
|
208
|
+
return;
|
|
206
209
|
window.open(url, '_blank');
|
|
207
210
|
};
|
|
208
211
|
this._updateFootnoteAttributes = (footnote) => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MarkdownASTToDeltaExtension } from '../../../affine-shared/adapters/index.js';
|
|
2
|
+
import { isSafeLinkUrl } from '../../../affine-shared/utils/index.js';
|
|
2
3
|
export const markdownLinkToDeltaMatcher = MarkdownASTToDeltaExtension({
|
|
3
4
|
name: 'link',
|
|
4
5
|
match: ast => ast.type === 'link',
|
|
@@ -39,6 +40,10 @@ export const markdownLinkToDeltaMatcher = MarkdownASTToDeltaExtension({
|
|
|
39
40
|
];
|
|
40
41
|
}
|
|
41
42
|
}
|
|
43
|
+
// A refused scheme (`javascript:`, `data:`…) keeps its text, loses the mark.
|
|
44
|
+
if (!isSafeLinkUrl(ast.url)) {
|
|
45
|
+
return ast.children.flatMap(child => context.toDelta(child));
|
|
46
|
+
}
|
|
42
47
|
return ast.children.flatMap(child => context.toDelta(child).map(delta => {
|
|
43
48
|
delta.attributes = { ...delta.attributes, link: ast.url };
|
|
44
49
|
return delta;
|
|
@@ -36,11 +36,11 @@ import { whenHover } from '../../affine-components/hover/index.js';
|
|
|
36
36
|
import { RefNodeSlotsProvider } from '../../affine-inline-reference/index.js';
|
|
37
37
|
import { ParseDocUrlProvider, ToolbarRegistryIdentifier, } from '../../affine-shared/services/index.js';
|
|
38
38
|
import { affineTextStyles } from '../../affine-shared/styles/index.js';
|
|
39
|
-
import { normalizeUrl } from '../../affine-shared/utils/index.js';
|
|
39
|
+
import { isSafeLinkUrl, normalizeUrl } from '../../affine-shared/utils/index.js';
|
|
40
40
|
import { WithDisposable } from '../../global/lit/index.js';
|
|
41
41
|
import { BLOCK_ID_ATTR, ShadowlessElement } from '../../std/index.js';
|
|
42
42
|
import { INLINE_ROOT_ATTR, ZERO_WIDTH_FOR_EMPTY_LINE, } from '../../std/inline/index.js';
|
|
43
|
-
import { css, html } from 'lit';
|
|
43
|
+
import { css, html, nothing } from 'lit';
|
|
44
44
|
import { property } from 'lit/decorators.js';
|
|
45
45
|
import { styleMap } from 'lit/directives/style-map.js';
|
|
46
46
|
let AffineLink = (() => {
|
|
@@ -112,8 +112,11 @@ let AffineLink = (() => {
|
|
|
112
112
|
this._referenceInfo = { pageId, params };
|
|
113
113
|
}
|
|
114
114
|
_renderLink(style) {
|
|
115
|
+
const { link } = this;
|
|
116
|
+
// A stored link may come from an imported or shared document: a refused
|
|
117
|
+
// scheme keeps its text but gets no href, so a click navigates nowhere.
|
|
115
118
|
return html `<a
|
|
116
|
-
href=${
|
|
119
|
+
href=${isSafeLinkUrl(link) ? link : nothing}
|
|
117
120
|
rel="noopener noreferrer"
|
|
118
121
|
target="_blank"
|
|
119
122
|
style=${styleMap(style)}
|
|
@@ -30,10 +30,12 @@ export * from './page-viewport-service.js';
|
|
|
30
30
|
export * from './parse-url-service.js';
|
|
31
31
|
export * from './pivot-materiality-publisher.js';
|
|
32
32
|
export * from './pivot-properties-service.js';
|
|
33
|
+
export * from './properties-panel-service.js';
|
|
33
34
|
export * from './quick-search-service.js';
|
|
34
35
|
export * from './selection-pane-service.js';
|
|
35
36
|
export * from './telemetry-service/index.js';
|
|
36
37
|
export * from './theme-service.js';
|
|
38
|
+
export * from './title-band-service.js';
|
|
37
39
|
export * from './toolbar-service/index.js';
|
|
38
40
|
export * from './translation-service/index.js';
|
|
39
41
|
export * from './universe-tag-defs-service.js';
|
|
@@ -30,10 +30,12 @@ export * from './page-viewport-service.js';
|
|
|
30
30
|
export * from './parse-url-service.js';
|
|
31
31
|
export * from './pivot-materiality-publisher.js';
|
|
32
32
|
export * from './pivot-properties-service.js';
|
|
33
|
+
export * from './properties-panel-service.js';
|
|
33
34
|
export * from './quick-search-service.js';
|
|
34
35
|
export * from './selection-pane-service.js';
|
|
35
36
|
export * from './telemetry-service/index.js';
|
|
36
37
|
export * from './theme-service.js';
|
|
38
|
+
export * from './title-band-service.js';
|
|
37
39
|
export * from './toolbar-service/index.js';
|
|
38
40
|
export * from './translation-service/index.js';
|
|
39
41
|
export * from './universe-tag-defs-service.js';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ExtensionType } from '../../store/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* What the properties panel is asked to show (ADR 0035 §9b): one panel with a
|
|
4
|
+
* typed request, like the artefact catalogue's `open(owner)`.
|
|
5
|
+
*
|
|
6
|
+
* - `page`: the property sheet of one document — the button beside the title
|
|
7
|
+
* band's "+" sends it, under the page title and in the note header alike.
|
|
8
|
+
* - `source`: the properties of one data source of a database, as a view
|
|
9
|
+
* shows them. Declared now so the host's `open` is written once against the
|
|
10
|
+
* whole union; the library sends it from the database view bar and the view
|
|
11
|
+
* settings when they ship (ADR 0035, issue B10), never before.
|
|
12
|
+
*
|
|
13
|
+
* The union only widens: a new scope is a new member, and an existing member
|
|
14
|
+
* never gains a required field.
|
|
15
|
+
*/
|
|
16
|
+
export type PropertiesPanelRequest = {
|
|
17
|
+
scope: 'page';
|
|
18
|
+
docId: string;
|
|
19
|
+
} | {
|
|
20
|
+
scope: 'source';
|
|
21
|
+
docId: string;
|
|
22
|
+
blockId: string;
|
|
23
|
+
sourceId: string;
|
|
24
|
+
viewId?: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The properties panel (ADR 0035 §9b), shaped as every panel seam of
|
|
28
|
+
* ADR 0034 §1: two verbs, nothing else. The host draws the panel where it
|
|
29
|
+
* wants it (the Labre app, in its left sidebar); the library never mounts one
|
|
30
|
+
* and keeps no "is it open" state, so a second `open` with another request
|
|
31
|
+
* retargets the host's panel rather than toggling anything here.
|
|
32
|
+
*
|
|
33
|
+
* Absent or `null`, the title band draws no panel button: a button that opens
|
|
34
|
+
* nothing is a lie. A database's properties stay reachable through the
|
|
35
|
+
* data-view's own properties popover.
|
|
36
|
+
*/
|
|
37
|
+
export interface PropertiesPanelService {
|
|
38
|
+
/** Show the panel for `request`. */
|
|
39
|
+
open(request: PropertiesPanelRequest): void;
|
|
40
|
+
/** Put it away. Writes nothing. */
|
|
41
|
+
close(): void;
|
|
42
|
+
}
|
|
43
|
+
export declare const PropertiesPanelProvider: import("../../global/di/types.js").ServiceIdentifier<PropertiesPanelService> & (<U extends PropertiesPanelService = PropertiesPanelService>(variant: import("../../global/di/types.js").ServiceVariant) => import("../../global/di/types.js").ServiceIdentifier<U>);
|
|
44
|
+
/**
|
|
45
|
+
* Host seam: the host's properties panel, or `null` to say there is none.
|
|
46
|
+
*
|
|
47
|
+
* `di.override`, like `OutlinePanelExtension`, so a later registration always
|
|
48
|
+
* wins and registering twice never throws at boot.
|
|
49
|
+
*/
|
|
50
|
+
export declare function PropertiesPanelExtension(service: PropertiesPanelService | null): ExtensionType;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createIdentifier } from '../../global/di/index.js';
|
|
2
|
+
export const PropertiesPanelProvider = createIdentifier('LabrePropertiesPanel');
|
|
3
|
+
/**
|
|
4
|
+
* Host seam: the host's properties panel, or `null` to say there is none.
|
|
5
|
+
*
|
|
6
|
+
* `di.override`, like `OutlinePanelExtension`, so a later registration always
|
|
7
|
+
* wins and registering twice never throws at boot.
|
|
8
|
+
*/
|
|
9
|
+
export function PropertiesPanelExtension(service) {
|
|
10
|
+
return {
|
|
11
|
+
setup: di => {
|
|
12
|
+
// `null` rides through the factory on purpose: `getOptional` then
|
|
13
|
+
// reports the panel as absent, which is the whole "no button" story.
|
|
14
|
+
di.override(PropertiesPanelProvider, () => service);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { ExtensionType } from '../../store/index.js';
|
|
2
|
+
import type { ReadonlySignal } from '@preact/signals-core';
|
|
3
|
+
/**
|
|
4
|
+
* `TitleBandProvider` — the one seam that feeds the band of property chips
|
|
5
|
+
* under the page title and at the head of the page's note on the canvas
|
|
6
|
+
* (ADR 0035 §9d and §11, REQ-PR-1 and PR-7).
|
|
7
|
+
*
|
|
8
|
+
* **No value lives in the document.** A page's property values are the
|
|
9
|
+
* host's (the Labre app keeps them in SQL, labre ADR-0047); the library reads
|
|
10
|
+
* them here, draws them, and hands every edit back through `set`. It never
|
|
11
|
+
* learns where they live, never caches them and never applies a value itself:
|
|
12
|
+
* the host's store does, and owns the rollback.
|
|
13
|
+
*
|
|
14
|
+
* Not `PivotPropertiesProvider` (ADR 0006): that seam is keyed by an element's
|
|
15
|
+
* `pivotDocId`, read-only, with its own value union; this one is keyed by a
|
|
16
|
+
* document, written, and speaks the data-view property types, so a chip and a
|
|
17
|
+
* database cell read the same `type`, `data` and value.
|
|
18
|
+
*
|
|
19
|
+
* Absent, there is no band at all: no chips, no "+", no panel button, no "Not
|
|
20
|
+
* shown in this document" row, and nothing else changes.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* One visible property of a page's sheet, in the order the band draws it.
|
|
24
|
+
*
|
|
25
|
+
* `type` and `data` are a data-view property type and its config (`select`
|
|
26
|
+
* with `{ options }`, `number` with `{ format }`, …); `value` is the JSON a
|
|
27
|
+
* data-view cell of that type holds (`select`: an option id, `multi-select`:
|
|
28
|
+
* option ids, `date`: epoch milliseconds, `checkbox`: a boolean, text types: a
|
|
29
|
+
* string). `null` or `undefined` is an empty value, drawn as an empty chip
|
|
30
|
+
* that can still be clicked (REQ-PR-1).
|
|
31
|
+
*
|
|
32
|
+
* Visibility is resolved by the host: what is in the list is visible, in that
|
|
33
|
+
* order, including a visible property with no value.
|
|
34
|
+
*/
|
|
35
|
+
export interface TitleBandProperty {
|
|
36
|
+
/** Stable, host-defined id; what `set` receives back. */
|
|
37
|
+
id: string;
|
|
38
|
+
/** Whose definition this is: the workspace's, or the database's of a row. */
|
|
39
|
+
namespace: 'workspace' | 'database';
|
|
40
|
+
/** Display name, already localized by the host. */
|
|
41
|
+
name: string;
|
|
42
|
+
/** A data-view property type (`text`, `number`, `select`, …). */
|
|
43
|
+
type: string;
|
|
44
|
+
/** That type's config, as a data-view property holds it. */
|
|
45
|
+
data: Record<string, unknown>;
|
|
46
|
+
/** The JSON a data-view cell of that type holds; nullish is empty. */
|
|
47
|
+
value: unknown;
|
|
48
|
+
/** A read-only property draws its chip and opens no editor. */
|
|
49
|
+
readonly: boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The state of one page's sheet. Same four states as ADR 0006: the signal may
|
|
53
|
+
* start at `loading` and settle later, and the library never awaits it.
|
|
54
|
+
*/
|
|
55
|
+
export type TitleBandSheetState = {
|
|
56
|
+
status: 'loading';
|
|
57
|
+
} | {
|
|
58
|
+
status: 'ready';
|
|
59
|
+
properties: readonly TitleBandProperty[];
|
|
60
|
+
}
|
|
61
|
+
/** The page has no sheet this viewer may read. The band draws nothing. */
|
|
62
|
+
| {
|
|
63
|
+
status: 'missing';
|
|
64
|
+
} | {
|
|
65
|
+
status: 'error';
|
|
66
|
+
reason?: string;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* A child page this document holds but does not display (labre ADR-0045):
|
|
70
|
+
* the host decides what belongs here, the library never computes a parent or
|
|
71
|
+
* a child.
|
|
72
|
+
*/
|
|
73
|
+
export interface TitleBandNotShownPage {
|
|
74
|
+
docId: string;
|
|
75
|
+
/** The page's title; an empty one is drawn as "Untitled". */
|
|
76
|
+
title: string;
|
|
77
|
+
}
|
|
78
|
+
export interface TitleBandService {
|
|
79
|
+
/**
|
|
80
|
+
* The sheet of document `docId`. MUST return synchronously and MUST NOT
|
|
81
|
+
* throw; the same signal for the same document is the host's business (the
|
|
82
|
+
* band asks once per mounted document).
|
|
83
|
+
*/
|
|
84
|
+
sheet$(docId: string): ReadonlySignal<TitleBandSheetState>;
|
|
85
|
+
/**
|
|
86
|
+
* Write one value. The band shows the chip as pending until the promise
|
|
87
|
+
* settles and never applies the value itself: the new value arrives through
|
|
88
|
+
* `sheet$`. A rejection is the host's to report and roll back; the band only
|
|
89
|
+
* clears its pending state. `value` is `null` to clear.
|
|
90
|
+
*/
|
|
91
|
+
set(docId: string, propertyId: string, value: unknown): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* The band's "+" (REQ-PR-4): what it does is the host's (open the
|
|
94
|
+
* workspace's property settings, or create a database property directly
|
|
95
|
+
* on a row). Absent, there is no "+".
|
|
96
|
+
*/
|
|
97
|
+
add?(docId: string): void;
|
|
98
|
+
/**
|
|
99
|
+
* Whether the band is collapsed for this document. The host decides where
|
|
100
|
+
* that is remembered (the Labre app: per device, REQ-PR-6). Absent, the
|
|
101
|
+
* band opens expanded every time and a collapse lasts while it is mounted.
|
|
102
|
+
*/
|
|
103
|
+
collapsed$?(docId: string): ReadonlySignal<boolean>;
|
|
104
|
+
setCollapsed?(docId: string, collapsed: boolean): void;
|
|
105
|
+
/**
|
|
106
|
+
* The child pages this document holds but does not display. Read **in the
|
|
107
|
+
* canvas view only**, drawn in the note header as a "Not shown in this
|
|
108
|
+
* document" row of page links that open through the linked-document path
|
|
109
|
+
* (`RefNodeSlotsProvider.docLinkClicked`). In the page view the host draws
|
|
110
|
+
* that row under the title itself, so a document never shows two. Absent
|
|
111
|
+
* or empty, no row.
|
|
112
|
+
*/
|
|
113
|
+
notShown$?(docId: string): ReadonlySignal<readonly TitleBandNotShownPage[]>;
|
|
114
|
+
}
|
|
115
|
+
export declare const TitleBandProvider: import("../../global/di/types.js").ServiceIdentifier<TitleBandService> & (<U extends TitleBandService = TitleBandService>(variant: import("../../global/di/types.js").ServiceVariant) => import("../../global/di/types.js").ServiceIdentifier<U>);
|
|
116
|
+
/**
|
|
117
|
+
* Host seam: the service that feeds the title band. **No default is
|
|
118
|
+
* registered**: absence is a meaningful state (the playground without a mock,
|
|
119
|
+
* a host with no properties), and the band then draws nothing.
|
|
120
|
+
*
|
|
121
|
+
* `di.override`, so a later registration always wins and registering twice
|
|
122
|
+
* never throws at boot.
|
|
123
|
+
*/
|
|
124
|
+
export declare function TitleBandExtension(service: TitleBandService): ExtensionType;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createIdentifier } from '../../global/di/index.js';
|
|
2
|
+
export const TitleBandProvider = createIdentifier('LabreTitleBand');
|
|
3
|
+
/**
|
|
4
|
+
* Host seam: the service that feeds the title band. **No default is
|
|
5
|
+
* registered**: absence is a meaningful state (the playground without a mock,
|
|
6
|
+
* a host with no properties), and the band then draws nothing.
|
|
7
|
+
*
|
|
8
|
+
* `di.override`, so a later registration always wins and registering twice
|
|
9
|
+
* never throws at boot.
|
|
10
|
+
*/
|
|
11
|
+
export function TitleBandExtension(service) {
|
|
12
|
+
return {
|
|
13
|
+
setup: di => {
|
|
14
|
+
di.override(TitleBandProvider, () => service);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
export declare function normalizeUrl(str: string): string;
|
|
2
|
+
/**
|
|
3
|
+
* Whether a link stored in a document may become an `href` or be opened.
|
|
4
|
+
*
|
|
5
|
+
* Only `http:`, `https:` and `mailto:` pass, plus scheme-less values that
|
|
6
|
+
* {@link normalizeUrl} turns into https (`example.com`). Everything else is
|
|
7
|
+
* refused — `javascript:`, `data:`, `vbscript:`, `file:`, relative paths —
|
|
8
|
+
* because the value comes from an imported, pasted or shared document and a
|
|
9
|
+
* click on it must never run in the editor's origin. The scheme is read by
|
|
10
|
+
* `new URL` after the same cleanup the browser does, never by a prefix test,
|
|
11
|
+
* so ` JaVaScRiPt:` and `java\tscript:` are recognised for what they are.
|
|
12
|
+
*
|
|
13
|
+
* Render and open sites call this on the stored value; nothing is rewritten
|
|
14
|
+
* on load, so a refused link keeps its text and simply is not clickable.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isSafeLinkUrl(str: string | null | undefined): str is string;
|
|
2
17
|
/**
|
|
3
18
|
* Assume user will input a url, we just need to check if it is valid.
|
|
4
19
|
*
|
|
@@ -57,6 +57,36 @@ export function normalizeUrl(str) {
|
|
|
57
57
|
}
|
|
58
58
|
return str;
|
|
59
59
|
}
|
|
60
|
+
const LINK_SCHEMES = new Set(['http:', 'https:', 'mailto:']);
|
|
61
|
+
// What the WHATWG URL parser drops before reading a scheme: leading and
|
|
62
|
+
// trailing C0 controls and spaces, and every tab or newline anywhere.
|
|
63
|
+
const URL_EDGE_JUNK = /^[\u0000-\u0020]+|[\u0000-\u0020]+$/g;
|
|
64
|
+
const URL_TAB_OR_NEWLINE = /[\t\n\r]/g;
|
|
65
|
+
/**
|
|
66
|
+
* Whether a link stored in a document may become an `href` or be opened.
|
|
67
|
+
*
|
|
68
|
+
* Only `http:`, `https:` and `mailto:` pass, plus scheme-less values that
|
|
69
|
+
* {@link normalizeUrl} turns into https (`example.com`). Everything else is
|
|
70
|
+
* refused — `javascript:`, `data:`, `vbscript:`, `file:`, relative paths —
|
|
71
|
+
* because the value comes from an imported, pasted or shared document and a
|
|
72
|
+
* click on it must never run in the editor's origin. The scheme is read by
|
|
73
|
+
* `new URL` after the same cleanup the browser does, never by a prefix test,
|
|
74
|
+
* so ` JaVaScRiPt:` and `java\tscript:` are recognised for what they are.
|
|
75
|
+
*
|
|
76
|
+
* Render and open sites call this on the stored value; nothing is rewritten
|
|
77
|
+
* on load, so a refused link keeps its text and simply is not clickable.
|
|
78
|
+
*/
|
|
79
|
+
export function isSafeLinkUrl(str) {
|
|
80
|
+
if (!str)
|
|
81
|
+
return false;
|
|
82
|
+
const cleaned = str
|
|
83
|
+
.replace(URL_TAB_OR_NEWLINE, '')
|
|
84
|
+
.replace(URL_EDGE_JUNK, '');
|
|
85
|
+
if (!cleaned)
|
|
86
|
+
return false;
|
|
87
|
+
const url = toURL(normalizeUrl(cleaned));
|
|
88
|
+
return url !== null && LINK_SCHEMES.has(url.protocol);
|
|
89
|
+
}
|
|
60
90
|
/**
|
|
61
91
|
* Assume user will input a url, we just need to check if it is valid.
|
|
62
92
|
*
|
|
@@ -34,7 +34,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
|
|
|
34
34
|
};
|
|
35
35
|
import { RefNodeSlotsProvider } from '../affine-inline-reference/index.js';
|
|
36
36
|
import { translateKey } from '../affine-shared/services/index.js';
|
|
37
|
-
import { overlayScale, requestThrottledConnectedFrame, toOverlayCoord, } from '../affine-shared/utils/index.js';
|
|
37
|
+
import { isSafeLinkUrl, overlayScale, requestThrottledConnectedFrame, toOverlayCoord, } from '../affine-shared/utils/index.js';
|
|
38
38
|
import { WidgetComponent } from '../std/index.js';
|
|
39
39
|
import { GfxControllerIdentifier, GfxPrimitiveElementModel, } from '../std/gfx/index.js';
|
|
40
40
|
import { OpenInNewIcon, RightSidebarIcon } from '@blocksuite/icons/lit';
|
|
@@ -94,7 +94,7 @@ let EdgelessElementLinkWidget = (() => {
|
|
|
94
94
|
host: this.std.host,
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
|
-
else if (el.externalLink) {
|
|
97
|
+
else if (isSafeLinkUrl(el.externalLink)) {
|
|
98
98
|
window.open(el.externalLink, '_blank', 'noopener,noreferrer');
|
|
99
99
|
}
|
|
100
100
|
};
|
|
@@ -52,29 +52,29 @@ let AffineEdgelessZoomToolbarWidget = (() => {
|
|
|
52
52
|
__esDecorate(this, null, __hide_decorators, { kind: "accessor", name: "_hide", static: false, private: false, access: { has: obj => "_hide" in obj, get: obj => obj._hide, set: (obj, value) => { obj._hide = value; } }, metadata: _metadata }, __hide_initializers, __hide_extraInitializers);
|
|
53
53
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
54
54
|
}
|
|
55
|
-
static { this.styles = css `
|
|
56
|
-
:host {
|
|
57
|
-
position: absolute;
|
|
58
|
-
bottom: 20px;
|
|
59
|
-
left: 12px;
|
|
60
|
-
z-index: var(--affine-z-index-popover);
|
|
61
|
-
display: flex;
|
|
62
|
-
justify-content: center;
|
|
63
|
-
-webkit-user-select: none;
|
|
64
|
-
user-select: none;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
@container viewport (width <= 1200px) {
|
|
68
|
-
edgeless-zoom-toolbar {
|
|
69
|
-
display: none;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
@container viewport (width > 1200px) {
|
|
74
|
-
zoom-bar-toggle-button {
|
|
75
|
-
display: none;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
55
|
+
static { this.styles = css `
|
|
56
|
+
:host {
|
|
57
|
+
position: absolute;
|
|
58
|
+
bottom: 20px;
|
|
59
|
+
left: 12px;
|
|
60
|
+
z-index: var(--affine-z-index-popover);
|
|
61
|
+
display: flex;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
-webkit-user-select: none;
|
|
64
|
+
user-select: none;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@container viewport (width <= 1200px) {
|
|
68
|
+
edgeless-zoom-toolbar {
|
|
69
|
+
display: none;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@container viewport (width > 1200px) {
|
|
74
|
+
zoom-bar-toggle-button {
|
|
75
|
+
display: none;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
78
|
`; }
|
|
79
79
|
get edgeless() {
|
|
80
80
|
return this.block;
|
|
@@ -105,9 +105,9 @@ let AffineEdgelessZoomToolbarWidget = (() => {
|
|
|
105
105
|
if (this._hide || !this.edgeless) {
|
|
106
106
|
return nothing;
|
|
107
107
|
}
|
|
108
|
-
return html `
|
|
109
|
-
<edgeless-zoom-toolbar .std=${this.std}></edgeless-zoom-toolbar>
|
|
110
|
-
<zoom-bar-toggle-button .std=${this.std}></zoom-bar-toggle-button>
|
|
108
|
+
return html `
|
|
109
|
+
<edgeless-zoom-toolbar .std=${this.std}></edgeless-zoom-toolbar>
|
|
110
|
+
<zoom-bar-toggle-button .std=${this.std}></zoom-bar-toggle-button>
|
|
111
111
|
`;
|
|
112
112
|
}
|
|
113
113
|
#_hide_accessor_storage = __runInitializers(this, __hide_initializers, false);
|