@quartz-community/bases-page 0.2.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/CHANGELOG.md +27 -0
- package/LICENSE +21 -0
- package/README.md +315 -0
- package/dist/BasesBody-B4CEJjEp.d.ts +6 -0
- package/dist/chunk-2AUMER56.js +44 -0
- package/dist/chunk-2AUMER56.js.map +1 -0
- package/dist/chunk-4HXXKSJ4.js +929 -0
- package/dist/chunk-4HXXKSJ4.js.map +1 -0
- package/dist/chunk-TDUJOYTU.js +42 -0
- package/dist/chunk-TDUJOYTU.js.map +1 -0
- package/dist/chunk-X2AZ5GOJ.js +1931 -0
- package/dist/chunk-X2AZ5GOJ.js.map +1 -0
- package/dist/compiler/index.d.ts +146 -0
- package/dist/compiler/index.js +7 -0
- package/dist/compiler/index.js.map +1 -0
- package/dist/components/index.d.ts +13 -0
- package/dist/components/index.js +9 -0
- package/dist/components/index.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +18703 -0
- package/dist/index.js.map +1 -0
- package/dist/registry.d.ts +48 -0
- package/dist/registry.js +7 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +181 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +143 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `options` field on `ViewTypeRegistration` — community plugins can pass user-supplied configuration (e.g. feature flags) that gets forwarded to every render invocation via `ViewRendererProps.options`.
|
|
13
|
+
- `options` field on `ViewRendererProps` — view renderers receive registration-time options as `Record<string, unknown>`.
|
|
14
|
+
- `slug`, `allSlugs`, and `linkResolution` fields on `ViewRendererProps` — view renderers can now resolve internal links correctly.
|
|
15
|
+
- `css` and `afterDOMLoaded` fields on `ViewTypeRegistration` — community view types can inject per-page styles and client-side scripts.
|
|
16
|
+
- `linkResolution` plugin option (`"absolute" | "relative" | "shortest"`) — controls how internal links are resolved in view renderers, matching the crawl-links plugin setting.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Added `yaml` and `unist-util-visit` to devDependencies (were listed as peer dependencies but missing from devDependencies, causing `tsc --noEmit` failures).
|
|
21
|
+
- Respect the `file.data.unlisted` convention in `resolveBasesEntries`. Pages marked `unlisted: true` are now excluded from every rendered base view (table, list, board, cards, gallery, and any custom view registered via `viewRegistry`), regardless of whether the base's filter expression would otherwise match them. Fixes a privacy leak where encrypted-unlisted and plain-unlisted pages showed up in base views built from queries like `file.ext == "md"` that matched the entire content directory. Both the main entry loop and the internal `fileLookup` used by `.asFile()` method resolution are filtered, so unlisted pages also cannot be dereferenced from formulas on visible pages. The convention is the same one respected by `content-index`, `search`, `backlinks`, `recent-notes`, `folder-page`, and `tag-page`, and is written by `@quartz-community/unlisted-pages` and `@quartz-community/encrypted-pages` (when `unlistWhenEncrypted` or per-page `unlisted: true`/`stealth: true` is set).
|
|
22
|
+
|
|
23
|
+
## [0.2.0]
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- Initial Quartz community plugin template.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Quartz Community
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# @quartz-community/bases-page
|
|
2
|
+
|
|
3
|
+
A page type and component plugin for Quartz v5 that renders Obsidian Bases (.base files) as database-like filtered views. This plugin (v0.2.0) provides a powerful way to visualize and interact with your Obsidian data in a structured format.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx quartz plugin add github:quartz-community/bases-page
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Enable the plugin in your Quartz configuration:
|
|
14
|
+
|
|
15
|
+
```yaml title="quartz.config.yaml"
|
|
16
|
+
plugins:
|
|
17
|
+
- source: github:quartz-community/bases-page
|
|
18
|
+
enabled: true
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Interaction with `unlisted` pages
|
|
22
|
+
|
|
23
|
+
`bases-page` respects the `file.data.unlisted` convention. Pages marked `unlisted: true` (by `@quartz-community/unlisted-pages`, or by `@quartz-community/encrypted-pages` via `unlistWhenEncrypted: true` or per-page `unlisted: true` / `stealth: true`) are excluded from every rendered base view — table, list, board, cards, gallery, and any custom view registered via `viewRegistry`. Both the main entry loop and the internal `fileLookup` used by `.asFile()` method resolution skip unlisted pages, so they cannot be dereferenced from formulas on visible pages either.
|
|
24
|
+
|
|
25
|
+
> [!note]
|
|
26
|
+
> Base views are **server-side rendered** HTML baked at build time. They do not update client-side after a visitor successfully decrypts an encrypted page. Graph, explorer, and search all re-hydrate from the patched in-memory content index after decryption and show newly-unlocked pages for the rest of the browser session — base views do not, because they were materialized at build time with unlisted pages already excluded. A visitor who decrypts a revealable encrypted page will see it appear in graph, explorer, and search, but **not** in any base view on the site, until the site is rebuilt with that page listed. This is the same structural limitation that applies to backlinks, recent notes, folder listings, and tag listings.
|
|
27
|
+
|
|
28
|
+
### TypeScript Override
|
|
29
|
+
|
|
30
|
+
For advanced use cases (e.g. custom view renderers), you can override in TypeScript:
|
|
31
|
+
|
|
32
|
+
```ts title="quartz.ts (override)"
|
|
33
|
+
import * as ExternalPlugin from "./.quartz/plugins";
|
|
34
|
+
|
|
35
|
+
ExternalPlugin.BasesPage({
|
|
36
|
+
defaultViewType: "table",
|
|
37
|
+
linkResolution: "shortest",
|
|
38
|
+
customViews: {
|
|
39
|
+
myView: ({
|
|
40
|
+
entries,
|
|
41
|
+
view,
|
|
42
|
+
basesData,
|
|
43
|
+
total,
|
|
44
|
+
locale,
|
|
45
|
+
slug,
|
|
46
|
+
allSlugs,
|
|
47
|
+
linkResolution,
|
|
48
|
+
options,
|
|
49
|
+
}) => {
|
|
50
|
+
// return JSX
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Built-in Views
|
|
57
|
+
|
|
58
|
+
- **Table**: Sortable columns with automatic type rendering for strings, numbers, booleans, arrays, and links. Supports custom column widths and row heights.
|
|
59
|
+
- **List**: A compact list view displaying metadata chips for each entry.
|
|
60
|
+
- **Cards**: A card-based layout with support for an optional image property and configurable card size or aspect ratio.
|
|
61
|
+
- **Gallery**: An image-focused grid layout designed for visual content with configurable card sizes.
|
|
62
|
+
- **Board**: A Kanban-style board that groups entries by a specific property (using boardProperty or groupBy), creating columns for each group value.
|
|
63
|
+
|
|
64
|
+
## Features
|
|
65
|
+
|
|
66
|
+
- Multiple views per .base file (switchable tabs)
|
|
67
|
+
- Recursive filter trees (and/or/not)
|
|
68
|
+
- Formulas (computed properties via expression engine)
|
|
69
|
+
- Summaries (column aggregations: Count, Average, Min, Max, Sum, Range, Median, Stddev, Earliest, Latest, Checked, Unchecked, Empty, Filled, Unique)
|
|
70
|
+
- Property display configuration
|
|
71
|
+
- Wikilink and Markdown link rendering
|
|
72
|
+
- Sort and grouping
|
|
73
|
+
|
|
74
|
+
## Expression Engine
|
|
75
|
+
|
|
76
|
+
The expression engine follows a pipeline of source, lexer, Pratt parser, AST, bytecode compiler, and stack interpreter.
|
|
77
|
+
|
|
78
|
+
### Supported Types
|
|
79
|
+
|
|
80
|
+
- Strings
|
|
81
|
+
- Numbers
|
|
82
|
+
- Booleans
|
|
83
|
+
- Null
|
|
84
|
+
- Lists
|
|
85
|
+
|
|
86
|
+
### Operators
|
|
87
|
+
|
|
88
|
+
- **Arithmetic**: +, -, \*, /
|
|
89
|
+
- **Comparison**: ==, !=, >, <, >=, <=
|
|
90
|
+
- **Logical**: and / &&, or / ||, not / !
|
|
91
|
+
|
|
92
|
+
### Property Access
|
|
93
|
+
|
|
94
|
+
- `note.property`: Access a property on the current note.
|
|
95
|
+
- `file.name`: The name of the file.
|
|
96
|
+
- `file.path`: The full path of the file.
|
|
97
|
+
- `file.folder`: The folder containing the file.
|
|
98
|
+
- `file.ext`: The file extension.
|
|
99
|
+
- `file.tags`: A list of tags associated with the file.
|
|
100
|
+
- `file.links`: A list of links in the file.
|
|
101
|
+
- `file.created`: The creation date of the file.
|
|
102
|
+
- `file.modified`: The last modification date of the file.
|
|
103
|
+
- `formula.name`: Access the result of another formula.
|
|
104
|
+
|
|
105
|
+
### Built-in Global Functions
|
|
106
|
+
|
|
107
|
+
- `if(condition, then, else)`
|
|
108
|
+
- `contains(container, item)`
|
|
109
|
+
- `date(string)`
|
|
110
|
+
- `duration(string)`
|
|
111
|
+
- `now()`
|
|
112
|
+
- `today()`
|
|
113
|
+
- `number(value)`
|
|
114
|
+
- `min(a, b, ...)`
|
|
115
|
+
- `max(a, b, ...)`
|
|
116
|
+
- `list(value)`
|
|
117
|
+
- `link(path, display?)`
|
|
118
|
+
- `image(url)`
|
|
119
|
+
- `icon(name)`
|
|
120
|
+
- `html(string)`
|
|
121
|
+
- `escapeHTML(string)`
|
|
122
|
+
- `file(path)`
|
|
123
|
+
|
|
124
|
+
### Method Functions
|
|
125
|
+
|
|
126
|
+
**Strings**
|
|
127
|
+
|
|
128
|
+
- `contains(substring)`
|
|
129
|
+
- `startsWith(prefix)`
|
|
130
|
+
- `endsWith(suffix)`
|
|
131
|
+
- `lower()`
|
|
132
|
+
- `upper()`
|
|
133
|
+
- `trim()`
|
|
134
|
+
- `replace(pattern, replacement)`
|
|
135
|
+
- `slice(start, end?)`
|
|
136
|
+
- `isEmpty()`
|
|
137
|
+
- `repeat(count)`
|
|
138
|
+
- `reverse()`
|
|
139
|
+
|
|
140
|
+
**Numbers**
|
|
141
|
+
|
|
142
|
+
- `toFixed(digits)`
|
|
143
|
+
- `round()`
|
|
144
|
+
- `floor()`
|
|
145
|
+
- `ceil()`
|
|
146
|
+
- `abs()`
|
|
147
|
+
|
|
148
|
+
**Dates**
|
|
149
|
+
|
|
150
|
+
- `format(formatString)`
|
|
151
|
+
- `year()`
|
|
152
|
+
- `month()`
|
|
153
|
+
- `day()`
|
|
154
|
+
- `time()`
|
|
155
|
+
- `relative()`
|
|
156
|
+
- `isEmpty()`
|
|
157
|
+
|
|
158
|
+
**Lists**
|
|
159
|
+
|
|
160
|
+
- `sum()`
|
|
161
|
+
- `mean()`
|
|
162
|
+
- `count()`
|
|
163
|
+
- `min()`
|
|
164
|
+
- `max()`
|
|
165
|
+
- `round()`
|
|
166
|
+
|
|
167
|
+
**File**
|
|
168
|
+
|
|
169
|
+
- `hasTag(tag)`
|
|
170
|
+
- `hasLink(link)`
|
|
171
|
+
- `inFolder(folder)`
|
|
172
|
+
- `hasProperty(property)`
|
|
173
|
+
|
|
174
|
+
## Extending with Custom Views
|
|
175
|
+
|
|
176
|
+
Community extensions can register custom view types using the view registry.
|
|
177
|
+
|
|
178
|
+
### Registering a View
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { viewRegistry } from "@quartz-community/bases-page/registry";
|
|
182
|
+
|
|
183
|
+
viewRegistry.register({
|
|
184
|
+
id: "my-custom-view",
|
|
185
|
+
name: "My Custom View",
|
|
186
|
+
icon: "star",
|
|
187
|
+
render: (props) => {
|
|
188
|
+
const { entries, view, basesData, total, locale, slug, allSlugs, linkResolution, options } =
|
|
189
|
+
props;
|
|
190
|
+
// Return your React/Preact component
|
|
191
|
+
},
|
|
192
|
+
css: `.my-custom-view { color: red; }`,
|
|
193
|
+
afterDOMLoaded: `console.log("My custom view loaded");`,
|
|
194
|
+
options: { enableFeatureX: true },
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Interfaces
|
|
199
|
+
|
|
200
|
+
**ViewTypeRegistration**
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
interface ViewTypeRegistration {
|
|
204
|
+
id: string;
|
|
205
|
+
name: string;
|
|
206
|
+
icon?: string;
|
|
207
|
+
render: ViewRenderer;
|
|
208
|
+
css?: string;
|
|
209
|
+
afterDOMLoaded?: string;
|
|
210
|
+
options?: Record<string, unknown>;
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**ViewRendererProps**
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
interface ViewRendererProps {
|
|
218
|
+
entries: BasesEntry[];
|
|
219
|
+
view: BasesView;
|
|
220
|
+
basesData: BasesData;
|
|
221
|
+
total: number;
|
|
222
|
+
locale: string;
|
|
223
|
+
slug: string;
|
|
224
|
+
allSlugs: string[];
|
|
225
|
+
linkResolution: "absolute" | "relative" | "shortest";
|
|
226
|
+
options?: Record<string, unknown>;
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Passing Options to a Custom View
|
|
231
|
+
|
|
232
|
+
Community plugins can use the `options` field to accept configuration at registration time. This is useful for feature flags, API keys, or other plugin-level settings.
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
import { viewRegistry } from "@quartz-community/bases-page/registry";
|
|
236
|
+
import type { LeafletMapPluginOptions } from "./types";
|
|
237
|
+
|
|
238
|
+
const defaultOptions: LeafletMapPluginOptions = {
|
|
239
|
+
enableCopyTool: false,
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export function registerLeafletMap(userOpts?: Partial<LeafletMapPluginOptions>): void {
|
|
243
|
+
const opts = { ...defaultOptions, ...userOpts };
|
|
244
|
+
viewRegistry.register({
|
|
245
|
+
...leafletMapViewRegistration,
|
|
246
|
+
options: opts,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
In the renderer, these options are available via `props.options` and should be cast to the plugin's options type.
|
|
252
|
+
|
|
253
|
+
## Compiler API
|
|
254
|
+
|
|
255
|
+
The expression engine is available as a public API for advanced usage. Import from `@quartz-community/bases-page/compiler`.
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import {
|
|
259
|
+
compile,
|
|
260
|
+
evaluate,
|
|
261
|
+
evaluateFilter,
|
|
262
|
+
resolvePropertyValue,
|
|
263
|
+
} from "@quartz-community/bases-page/compiler";
|
|
264
|
+
|
|
265
|
+
// Compile an expression to bytecode
|
|
266
|
+
const compiled = compile("note.price * 1.2");
|
|
267
|
+
|
|
268
|
+
// Evaluate an expression in a context
|
|
269
|
+
const result = evaluate("note.price * 1.2", context);
|
|
270
|
+
|
|
271
|
+
// Evaluate a filter node
|
|
272
|
+
const isMatch = evaluateFilter(filterNode, context);
|
|
273
|
+
|
|
274
|
+
// Resolve a property path
|
|
275
|
+
const value = resolvePropertyValue("file.name", context);
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### EvalContext Shape
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
interface EvalContext {
|
|
282
|
+
note: Record<string, unknown>;
|
|
283
|
+
file: {
|
|
284
|
+
name: string;
|
|
285
|
+
path: string;
|
|
286
|
+
folder: string;
|
|
287
|
+
ext: string;
|
|
288
|
+
tags: string[];
|
|
289
|
+
links: string[];
|
|
290
|
+
created?: string;
|
|
291
|
+
modified?: string;
|
|
292
|
+
};
|
|
293
|
+
formula: Record<string, unknown>;
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Package Exports
|
|
298
|
+
|
|
299
|
+
- `.`: Main plugin entry (BasesPage, BasesBody, viewRegistry, registerCustomViews, compiler API, types)
|
|
300
|
+
- `./types`: All type definitions
|
|
301
|
+
- `./components`: React/Preact components (BasesBody, ViewSelector)
|
|
302
|
+
- `./registry`: View registry singleton and registerCustomViews helper
|
|
303
|
+
- `./compiler`: Expression engine (compile, evaluate, evaluateFilter, resolvePropertyValue)
|
|
304
|
+
|
|
305
|
+
## Configuration Options
|
|
306
|
+
|
|
307
|
+
| Option | Type | Default | Description |
|
|
308
|
+
| :---------------- | :--------------------------------------- | :----------- | :--------------------------------------------------------------------------------------------- |
|
|
309
|
+
| `defaultViewType` | `string` | `"table"` | The default view type used when none is specified in the .base file. |
|
|
310
|
+
| `linkResolution` | `"absolute" \| "relative" \| "shortest"` | `"shortest"` | How to resolve internal links in view renderers. Should match your crawl-links plugin setting. |
|
|
311
|
+
| `customViews` | `Record<string, ViewRenderer>` | `{}` | Custom view renderers provided via TypeScript override. |
|
|
312
|
+
|
|
313
|
+
## License
|
|
314
|
+
|
|
315
|
+
MIT
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
|
|
3
|
+
createRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
// src/registry.ts
|
|
6
|
+
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("@quartz-community/bases-page/viewRegistry");
|
|
7
|
+
var ViewRegistry = class {
|
|
8
|
+
views = /* @__PURE__ */ new Map();
|
|
9
|
+
/**
|
|
10
|
+
* Register a view type. If a view with the same ID already exists it is
|
|
11
|
+
* silently replaced — this lets config-level `customViews` override built-in
|
|
12
|
+
* renderers for the same type.
|
|
13
|
+
*/
|
|
14
|
+
register(registration) {
|
|
15
|
+
this.views.set(registration.id, registration);
|
|
16
|
+
}
|
|
17
|
+
/** Look up a registered view type by ID. */
|
|
18
|
+
get(id) {
|
|
19
|
+
return this.views.get(id);
|
|
20
|
+
}
|
|
21
|
+
/** Return all registered view types (insertion order). */
|
|
22
|
+
getAll() {
|
|
23
|
+
return Array.from(this.views.values());
|
|
24
|
+
}
|
|
25
|
+
/** Check whether a view type is registered. */
|
|
26
|
+
has(id) {
|
|
27
|
+
return this.views.has(id);
|
|
28
|
+
}
|
|
29
|
+
/** Remove a view type registration. Returns true if it existed. */
|
|
30
|
+
unregister(id) {
|
|
31
|
+
return this.views.delete(id);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var g = globalThis;
|
|
35
|
+
var viewRegistry = g[REGISTRY_KEY] ??= new ViewRegistry();
|
|
36
|
+
function registerCustomViews(customs) {
|
|
37
|
+
for (const [id, render] of Object.entries(customs)) {
|
|
38
|
+
viewRegistry.register({ id, name: id, render });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { registerCustomViews, viewRegistry };
|
|
43
|
+
//# sourceMappingURL=chunk-2AUMER56.js.map
|
|
44
|
+
//# sourceMappingURL=chunk-2AUMER56.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/registry.ts"],"names":[],"mappings":";;;;;AAWA,IAAM,YAAA,mBAAe,MAAA,CAAO,GAAA,CAAI,2CAA2C,CAAA;AAqB3E,IAAM,eAAN,MAAmB;AAAA,EACT,KAAA,uBAAY,GAAA,EAAkC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOtD,SAAS,YAAA,EAA0C;AACjD,IAAA,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,YAAA,CAAa,EAAA,EAAI,YAAY,CAAA;AAAA,EAC9C;AAAA;AAAA,EAGA,IAAI,EAAA,EAA8C;AAChD,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,EAAE,CAAA;AAAA,EAC1B;AAAA;AAAA,EAGA,MAAA,GAAiC;AAC/B,IAAA,OAAO,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,QAAQ,CAAA;AAAA,EACvC;AAAA;AAAA,EAGA,IAAI,EAAA,EAAqB;AACvB,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,EAAE,CAAA;AAAA,EAC1B;AAAA;AAAA,EAGA,WAAW,EAAA,EAAqB;AAC9B,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,EAAE,CAAA;AAAA,EAC7B;AACF,CAAA;AASA,IAAM,CAAA,GAAI,UAAA;AACH,IAAM,YAAA,GAA8B,CAAA,CAAE,YAAY,CAAA,KAAM,IAAI,YAAA;AAM5D,SAAS,oBAAoB,OAAA,EAA6C;AAC/E,EAAA,KAAA,MAAW,CAAC,EAAA,EAAI,MAAM,KAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,EAAG;AAClD,IAAA,YAAA,CAAa,SAAS,EAAE,EAAA,EAAI,IAAA,EAAM,EAAA,EAAI,QAAQ,CAAA;AAAA,EAChD;AACF","file":"chunk-2AUMER56.js","sourcesContent":["import type { ViewTypeRegistration, ViewRenderer } from \"./types\";\n\n/**\n * Symbol key for the global ViewRegistry singleton.\n *\n * Using a well-known Symbol on `globalThis` guarantees that all copies of this\n * module — whether loaded from different `node_modules` paths or from different\n * tsup entry points — share the exact same registry instance. Without this,\n * community plugins that bundle or install their own copy of bases-page would\n * register views into a private registry that the main bases-page never sees.\n */\nconst REGISTRY_KEY = Symbol.for(\"@quartz-community/bases-page/viewRegistry\");\n\n/**\n * Central registry for Bases view types.\n *\n * Built-in views (table, list, cards, gallery, board) are registered at plugin\n * init time. Community plugins can register additional view types by importing\n * this singleton and calling {@link viewRegistry.register}.\n *\n * @example Community plugin registering a custom view:\n * ```ts\n * import { viewRegistry } from \"@quartz-community/bases-page\";\n *\n * viewRegistry.register({\n * id: \"timeline\",\n * name: \"Timeline\",\n * icon: \"git-branch\",\n * render: ({ entries, view }) => <div class=\"bases-timeline\">...</div>,\n * });\n * ```\n */\nclass ViewRegistry {\n private views = new Map<string, ViewTypeRegistration>();\n\n /**\n * Register a view type. If a view with the same ID already exists it is\n * silently replaced — this lets config-level `customViews` override built-in\n * renderers for the same type.\n */\n register(registration: ViewTypeRegistration): void {\n this.views.set(registration.id, registration);\n }\n\n /** Look up a registered view type by ID. */\n get(id: string): ViewTypeRegistration | undefined {\n return this.views.get(id);\n }\n\n /** Return all registered view types (insertion order). */\n getAll(): ViewTypeRegistration[] {\n return Array.from(this.views.values());\n }\n\n /** Check whether a view type is registered. */\n has(id: string): boolean {\n return this.views.has(id);\n }\n\n /** Remove a view type registration. Returns true if it existed. */\n unregister(id: string): boolean {\n return this.views.delete(id);\n }\n}\n\n/**\n * Singleton view registry instance, stored on `globalThis` via a well-known\n * Symbol so that all copies of this module share the same instance.\n *\n * Community plugins import this to register custom view types.\n * Built-in views are registered by `registerBuiltinViews()` during plugin init.\n */\nconst g = globalThis as unknown as Record<symbol, ViewRegistry>;\nexport const viewRegistry: ViewRegistry = (g[REGISTRY_KEY] ??= new ViewRegistry());\n\n/**\n * Convenience: bulk-register views from a `customViews` config record.\n * Called during plugin init to merge user-provided renderers into the registry.\n */\nexport function registerCustomViews(customs: Record<string, ViewRenderer>): void {\n for (const [id, render] of Object.entries(customs)) {\n viewRegistry.register({ id, name: id, render });\n }\n}\n"]}
|