@riebeckite/plugin-bases 0.0.1
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/LICENSE +201 -0
- package/README.md +180 -0
- package/README_ja.md +155 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +909 -0
- package/dist/src/evaluate.d.ts +6 -0
- package/dist/src/parse.d.ts +19 -0
- package/dist/src/placeholder.d.ts +21 -0
- package/dist/src/remark.d.ts +14 -0
- package/dist/src/render.d.ts +6 -0
- package/dist/src/runtime.d.ts +13 -0
- package/dist/src/types.d.ts +82 -0
- package/package.json +58 -0
- package/style.css +145 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ContentManifestEntry } from "@riebeckite/core";
|
|
2
|
+
import type { BasesCondition, BasesValueRef } from "./types.js";
|
|
3
|
+
/** Evaluates a compiled condition against one manifest entry. */
|
|
4
|
+
export declare function matchesCondition(condition: BasesCondition, entry: ContentManifestEntry): boolean;
|
|
5
|
+
/** Reads the entry value a filter reference points at. */
|
|
6
|
+
export declare function resolveValueRef(reference: BasesValueRef, entry: ContentManifestEntry): unknown;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BasesSpec, BasesValueRef } from "./types.js";
|
|
2
|
+
/** Columns used when neither the Base nor the view configures any. */
|
|
3
|
+
export declare const DEFAULT_COLUMNS: readonly ["file.name", "file.tags"];
|
|
4
|
+
type Result<T> = {
|
|
5
|
+
readonly ok: true;
|
|
6
|
+
readonly value: T;
|
|
7
|
+
} | {
|
|
8
|
+
readonly ok: false;
|
|
9
|
+
readonly message: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Compiles a parsed YAML Base document into the serializable spec the renderer
|
|
13
|
+
* consumes. Returns a diagnostic message when the document uses a shape the
|
|
14
|
+
* MVP cannot understand; the caller keeps the original code block in that case.
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseBases(document: unknown): Result<BasesSpec>;
|
|
17
|
+
/** Maps a property id (e.g. `file.name`, `note.status`, `draft`) to its ref. */
|
|
18
|
+
export declare function parseValueRef(raw: string): BasesValueRef;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BasesSpec } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build-time placeholder that stands in for a `base` code block until the
|
|
4
|
+
* content manifest (and therefore every entry's frontmatter, tags, links, and
|
|
5
|
+
* permalink) is available.
|
|
6
|
+
*
|
|
7
|
+
* The compiled spec and the raw block source are URI-encoded so they survive an
|
|
8
|
+
* HTML attribute round-trip through `rehype-raw` without quoting hazards. The
|
|
9
|
+
* attribute is removed when the runtime replaces the placeholder with rendered
|
|
10
|
+
* HTML, so it never reaches the published page.
|
|
11
|
+
*/
|
|
12
|
+
export declare const BASES_ATTRIBUTE = "data-rr-bases";
|
|
13
|
+
export type BasesPlaceholderPayload = {
|
|
14
|
+
readonly spec: BasesSpec;
|
|
15
|
+
readonly source: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function encodeBasesPayload(payload: BasesPlaceholderPayload): string;
|
|
18
|
+
export declare function decodeBasesPayload(encoded: string): BasesPlaceholderPayload | null;
|
|
19
|
+
export declare function createBasesPlaceholder(payload: BasesPlaceholderPayload): string;
|
|
20
|
+
/** A fresh global pattern is returned per call to avoid shared `lastIndex`. */
|
|
21
|
+
export declare function createBasesPlaceholderPattern(): RegExp;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Root } from "mdast";
|
|
2
|
+
export type RemarkBasesOptions = {
|
|
3
|
+
/** Fenced code block language treated as a Base. Defaults to `base`. */
|
|
4
|
+
language?: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Replaces fenced `base` code blocks with a build-time placeholder.
|
|
8
|
+
*
|
|
9
|
+
* The YAML body is parsed immediately so that invalid or unsupported
|
|
10
|
+
* definitions can be reported and left untouched (the code block survives);
|
|
11
|
+
* the compiled spec is carried inside the placeholder and rendered later, once
|
|
12
|
+
* the content manifest exists.
|
|
13
|
+
*/
|
|
14
|
+
export declare function remarkBases(options?: RemarkBasesOptions): (tree: Root, file: unknown) => void;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ContentManifestEntry } from "@riebeckite/core";
|
|
2
|
+
import type { BasesOptions, BasesSpec } from "./types.js";
|
|
3
|
+
/** Renders a compiled Base definition to HTML. */
|
|
4
|
+
export declare function renderBases(spec: BasesSpec, options: BasesOptions, entries: readonly ContentManifestEntry[], source: string): string;
|
|
5
|
+
/** Renders an inline error when a placeholder cannot be resolved. */
|
|
6
|
+
export declare function renderBasesError(message: string, options: BasesOptions): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ContentManifest, Diagnostic, PostContent, ResolvedRiebeckiteConfig } from "@riebeckite/core";
|
|
2
|
+
import type { BasesOptions } from "./types.js";
|
|
3
|
+
export type BasesRuntime = {
|
|
4
|
+
/**
|
|
5
|
+
* Remembers the processed content object for a slug. `ContentManager` caches
|
|
6
|
+
* and reuses this exact object, so replacing its `html` here is what makes the
|
|
7
|
+
* rewrite visible to consumers that render `getProcessedContent()`.
|
|
8
|
+
*/
|
|
9
|
+
track(slug: string, content: PostContent): void;
|
|
10
|
+
/** Replaces every Base placeholder in the manifest with rendered output. */
|
|
11
|
+
resolve(manifest: ContentManifest, diagnostics: Diagnostic[], config?: ResolvedRiebeckiteConfig): void;
|
|
12
|
+
};
|
|
13
|
+
export declare function createBasesRuntime(options: BasesOptions): BasesRuntime;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { ContentQuerySort } from "@riebeckite/core";
|
|
2
|
+
/** View kinds supported by the MVP renderer. */
|
|
3
|
+
export type BasesViewType = "table" | "cards";
|
|
4
|
+
/** Scalar literal that can appear on the right-hand side of a comparison. */
|
|
5
|
+
export type BasesLiteral = string | number | boolean | null;
|
|
6
|
+
/** Comparison operators supported in filter expressions. */
|
|
7
|
+
export type BasesOperator = "==" | "!=" | ">" | "<" | ">=" | "<=" | "contains";
|
|
8
|
+
/** Built-in `file.*` values exposed to filter expressions. */
|
|
9
|
+
export type BasesBuiltinValue = "file.name" | "file.path" | "file.slug" | "file.folder" | "file.title" | "file.link" | "file.permalink" | "file.tags";
|
|
10
|
+
/** A value read either from a built-in or from frontmatter. */
|
|
11
|
+
export type BasesValueRef = {
|
|
12
|
+
readonly source: "builtin";
|
|
13
|
+
readonly name: BasesBuiltinValue;
|
|
14
|
+
} | {
|
|
15
|
+
readonly source: "frontmatter";
|
|
16
|
+
readonly key: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* A compiled Base filter condition. The tree mirrors the `and` / `or` / `not`
|
|
20
|
+
* shape of an Obsidian Base and is evaluated against manifest entries.
|
|
21
|
+
*/
|
|
22
|
+
export type BasesCondition = {
|
|
23
|
+
readonly kind: "and";
|
|
24
|
+
readonly conditions: readonly BasesCondition[];
|
|
25
|
+
} | {
|
|
26
|
+
readonly kind: "or";
|
|
27
|
+
readonly conditions: readonly BasesCondition[];
|
|
28
|
+
} | {
|
|
29
|
+
readonly kind: "not";
|
|
30
|
+
readonly condition: BasesCondition;
|
|
31
|
+
} | {
|
|
32
|
+
readonly kind: "hasTag";
|
|
33
|
+
readonly tag: string;
|
|
34
|
+
} | {
|
|
35
|
+
readonly kind: "inFolder";
|
|
36
|
+
readonly folder: string;
|
|
37
|
+
} | {
|
|
38
|
+
readonly kind: "hasLink";
|
|
39
|
+
readonly link: string;
|
|
40
|
+
} | {
|
|
41
|
+
readonly kind: "compare";
|
|
42
|
+
readonly left: BasesValueRef;
|
|
43
|
+
readonly operator: BasesOperator;
|
|
44
|
+
readonly right: BasesLiteral;
|
|
45
|
+
};
|
|
46
|
+
/** A compiled Base view. */
|
|
47
|
+
export type BasesView = {
|
|
48
|
+
readonly type: BasesViewType;
|
|
49
|
+
/** Optional human-readable label; rendered above the view output. */
|
|
50
|
+
readonly name?: string;
|
|
51
|
+
/** Property ids used as table columns (or card fields). */
|
|
52
|
+
readonly columns: readonly string[];
|
|
53
|
+
/** View-local filter, combined with the top-level filter using AND. */
|
|
54
|
+
readonly filter?: BasesCondition;
|
|
55
|
+
/** Sort keys, mapped onto the Core content query sort shape. */
|
|
56
|
+
readonly sort?: readonly ContentQuerySort[];
|
|
57
|
+
/** View-local row cap. Still bounded by {@link BasesOptions.limit}. */
|
|
58
|
+
readonly limit?: number;
|
|
59
|
+
};
|
|
60
|
+
/** A compiled Base definition. */
|
|
61
|
+
export type BasesSpec = {
|
|
62
|
+
/** Top-level filter applied to every view. */
|
|
63
|
+
readonly filter?: BasesCondition;
|
|
64
|
+
/** Property id -> display label. */
|
|
65
|
+
readonly properties: Readonly<Record<string, string>>;
|
|
66
|
+
readonly views: readonly BasesView[];
|
|
67
|
+
};
|
|
68
|
+
export type BasesOptions = {
|
|
69
|
+
/** Root CSS class. Defaults to `rb-bases`. */
|
|
70
|
+
className?: string;
|
|
71
|
+
/** Fenced code block language. Defaults to `base`. */
|
|
72
|
+
language?: string;
|
|
73
|
+
/** Global row cap applied to every view. Defaults to `100`. */
|
|
74
|
+
limit?: number;
|
|
75
|
+
/** Render the raw Base definition in a `<details>` fallback. Defaults to `true`. */
|
|
76
|
+
showFallback?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Name of the single view to render when a Base defines several. When unset,
|
|
79
|
+
* every view is rendered as a labelled section.
|
|
80
|
+
*/
|
|
81
|
+
view?: string;
|
|
82
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@riebeckite/plugin-bases",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"source": "./index.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./style.css": "./style.css"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@types/mdast": "^4.0.4",
|
|
19
|
+
"unist-util-visit": "^5.1.0",
|
|
20
|
+
"vfile-matter": "^5.0.1",
|
|
21
|
+
"@riebeckite/core": "0.0.1"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"LICENSE",
|
|
28
|
+
"README.md",
|
|
29
|
+
"README_ja.md",
|
|
30
|
+
"dist",
|
|
31
|
+
"style.css"
|
|
32
|
+
],
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/Rerurate514/riebeckite.git",
|
|
36
|
+
"directory": "packages/plugins/bases"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/Rerurate514/riebeckite#readme",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/Rerurate514/riebeckite/issues"
|
|
41
|
+
},
|
|
42
|
+
"description": "Build-time rendering for Obsidian Bases definitions.",
|
|
43
|
+
"keywords": [
|
|
44
|
+
"riebeckite",
|
|
45
|
+
"plugin",
|
|
46
|
+
"obsidian",
|
|
47
|
+
"bases"
|
|
48
|
+
],
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
51
|
+
},
|
|
52
|
+
"sideEffects": [
|
|
53
|
+
"./style.css"
|
|
54
|
+
],
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "node ../../../scripts/build_package.mjs"
|
|
57
|
+
}
|
|
58
|
+
}
|
package/style.css
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
.rb-bases {
|
|
2
|
+
--rb-bases-ink: var(--rb-color-ink, #000000);
|
|
3
|
+
--rb-bases-paper: var(--rb-color-paper, #ffffff);
|
|
4
|
+
--rb-bases-muted: var(--rb-color-muted, #69645c);
|
|
5
|
+
--rb-bases-accent: var(--rb-color-accent, #686868);
|
|
6
|
+
--rb-bases-rule: var(
|
|
7
|
+
--rb-color-border,
|
|
8
|
+
color-mix(in srgb, var(--rb-bases-ink) 18%, transparent)
|
|
9
|
+
);
|
|
10
|
+
--rb-bases-surface: color-mix(
|
|
11
|
+
in srgb,
|
|
12
|
+
var(--rb-bases-paper) 94%,
|
|
13
|
+
var(--rb-bases-ink)
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
margin: 1.5rem 0;
|
|
17
|
+
color: var(--rb-bases-ink);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.rb-bases__view + .rb-bases__view {
|
|
21
|
+
margin-top: 1.5rem;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.rb-bases__view-name {
|
|
25
|
+
margin: 0 0 0.5rem;
|
|
26
|
+
font-size: 0.9375rem;
|
|
27
|
+
font-weight: 700;
|
|
28
|
+
letter-spacing: 0.01em;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.rb-bases__table {
|
|
32
|
+
width: 100%;
|
|
33
|
+
border-collapse: collapse;
|
|
34
|
+
font-size: 0.9375rem;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.rb-bases__heading {
|
|
38
|
+
padding: 0.5rem 0.75rem;
|
|
39
|
+
border-bottom: 1px solid var(--rb-bases-ink);
|
|
40
|
+
text-align: left;
|
|
41
|
+
font-weight: 700;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.rb-bases__cell {
|
|
45
|
+
padding: 0.5rem 0.75rem;
|
|
46
|
+
vertical-align: top;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.rb-bases__row:not(:last-child) .rb-bases__cell {
|
|
50
|
+
border-bottom: 1px solid var(--rb-bases-rule);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.rb-bases__link {
|
|
54
|
+
color: inherit;
|
|
55
|
+
text-decoration: underline;
|
|
56
|
+
text-decoration-color: var(--rb-bases-rule);
|
|
57
|
+
text-underline-offset: 0.2em;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.rb-bases__link:hover,
|
|
61
|
+
.rb-bases__link:focus-visible {
|
|
62
|
+
text-decoration-color: var(--rb-bases-accent);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.rb-bases__tag {
|
|
66
|
+
margin-right: 0.35rem;
|
|
67
|
+
color: var(--rb-bases-muted);
|
|
68
|
+
font-size: 0.8125em;
|
|
69
|
+
white-space: nowrap;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.rb-bases__cards {
|
|
73
|
+
display: grid;
|
|
74
|
+
grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
|
|
75
|
+
gap: 0.75rem;
|
|
76
|
+
margin: 0;
|
|
77
|
+
padding: 0;
|
|
78
|
+
list-style: none;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.rb-bases__card {
|
|
82
|
+
padding: 0.75rem 1rem;
|
|
83
|
+
border: 1px solid var(--rb-bases-rule);
|
|
84
|
+
background: var(--rb-bases-surface);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.rb-bases__card-title {
|
|
88
|
+
font-weight: 700;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.rb-bases__fields {
|
|
92
|
+
display: grid;
|
|
93
|
+
grid-template-columns: auto 1fr;
|
|
94
|
+
gap: 0.15rem 0.75rem;
|
|
95
|
+
margin: 0.5rem 0 0;
|
|
96
|
+
font-size: 0.875rem;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.rb-bases__field {
|
|
100
|
+
display: contents;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.rb-bases__field-label {
|
|
104
|
+
color: var(--rb-bases-muted);
|
|
105
|
+
font-weight: 400;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.rb-bases__field-value {
|
|
109
|
+
margin: 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.rb-bases__empty {
|
|
113
|
+
margin: 0;
|
|
114
|
+
color: var(--rb-bases-muted);
|
|
115
|
+
font-style: italic;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.rb-bases__fallback {
|
|
119
|
+
margin-top: 1rem;
|
|
120
|
+
color: var(--rb-bases-muted);
|
|
121
|
+
font-size: 0.8125rem;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.rb-bases__fallback pre {
|
|
125
|
+
overflow-x: auto;
|
|
126
|
+
margin: 0.5rem 0 0;
|
|
127
|
+
padding: 0.75rem;
|
|
128
|
+
border: 1px solid var(--rb-bases-rule);
|
|
129
|
+
background: var(--rb-bases-surface);
|
|
130
|
+
font-size: 0.8125rem;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.rb-bases--error {
|
|
134
|
+
margin: 1.5rem 0;
|
|
135
|
+
padding: 0.75rem 1rem;
|
|
136
|
+
border: 1px solid var(--rb-bases-ink);
|
|
137
|
+
background: var(--rb-bases-surface);
|
|
138
|
+
font-size: 0.875rem;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
html[data-theme="dark"] .rb-bases,
|
|
142
|
+
html.dark .rb-bases {
|
|
143
|
+
--rb-bases-ink: var(--rb-color-paper, #ffffff);
|
|
144
|
+
--rb-bases-paper: var(--rb-color-ink, #000000);
|
|
145
|
+
}
|