react-obsidian-md 0.0.0-stage → 0.1.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.
Files changed (59) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +26 -2
  3. package/dist/collapsible.d.ts +6 -0
  4. package/dist/collapsible.d.ts.map +1 -0
  5. package/dist/collapsible.js +14 -0
  6. package/dist/collapsible.js.map +1 -0
  7. package/dist/formatted-date.d.ts +17 -0
  8. package/dist/formatted-date.d.ts.map +1 -0
  9. package/dist/formatted-date.js +34 -0
  10. package/dist/formatted-date.js.map +1 -0
  11. package/dist/hover-preview.d.ts +13 -0
  12. package/dist/hover-preview.d.ts.map +1 -0
  13. package/dist/hover-preview.js +107 -0
  14. package/dist/hover-preview.js.map +1 -0
  15. package/dist/index.d.ts +14 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +14 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/preview-body.d.ts +8 -0
  20. package/dist/preview-body.d.ts.map +1 -0
  21. package/dist/preview-body.js +34 -0
  22. package/dist/preview-body.js.map +1 -0
  23. package/dist/preview-client.d.ts +9 -0
  24. package/dist/preview-client.d.ts.map +1 -0
  25. package/dist/preview-client.js +27 -0
  26. package/dist/preview-client.js.map +1 -0
  27. package/dist/preview-slice.d.ts +11 -0
  28. package/dist/preview-slice.d.ts.map +1 -0
  29. package/dist/preview-slice.js +67 -0
  30. package/dist/preview-slice.js.map +1 -0
  31. package/dist/preview-types.d.ts +19 -0
  32. package/dist/preview-types.d.ts.map +1 -0
  33. package/dist/preview-types.js +13 -0
  34. package/dist/preview-types.js.map +1 -0
  35. package/dist/properties-keys.d.ts +4 -0
  36. package/dist/properties-keys.d.ts.map +1 -0
  37. package/dist/properties-keys.js +4 -0
  38. package/dist/properties-keys.js.map +1 -0
  39. package/dist/properties-panel.d.ts +22 -0
  40. package/dist/properties-panel.d.ts.map +1 -0
  41. package/dist/properties-panel.js +24 -0
  42. package/dist/properties-panel.js.map +1 -0
  43. package/dist/properties.d.ts +42 -0
  44. package/dist/properties.d.ts.map +1 -0
  45. package/dist/properties.js +137 -0
  46. package/dist/properties.js.map +1 -0
  47. package/dist/provider.d.ts +34 -0
  48. package/dist/provider.d.ts.map +1 -0
  49. package/dist/provider.js +24 -0
  50. package/dist/provider.js.map +1 -0
  51. package/dist/slug-key.d.ts +5 -0
  52. package/dist/slug-key.d.ts.map +1 -0
  53. package/dist/slug-key.js +9 -0
  54. package/dist/slug-key.js.map +1 -0
  55. package/dist/use-persisted-open.d.ts +17 -0
  56. package/dist/use-persisted-open.d.ts.map +1 -0
  57. package/dist/use-persisted-open.js +58 -0
  58. package/dist/use-persisted-open.js.map +1 -0
  59. package/package.json +68 -3
package/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Adriano Araújo (adrianoaraujodsantos@hotmail.com).
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,27 @@
1
- # Temporary Holding Version
1
+ # react-obsidian-md
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Headless React components for Obsidian.md content: a collapsible **Properties Panel** with persisted state, a **Hover Preview** popover, and the client helpers that fetch a **Preview Payload**. It ships no CSS; style it with your own.
4
+
5
+ Terms follow the [glossary](../../GLOSSARY.md). Pair it with [`remark-obsidian-md`](../remark-obsidian-md), and with [`fumadocs-obsidian-md`](../fumadocs-obsidian-md) for the Fumadocs side of previews.
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ pnpm add react-obsidian-md
11
+ ```
12
+
13
+ `react`, `react-dom` and `remark-obsidian-md` are peer dependencies.
14
+
15
+ ## What it exports
16
+
17
+ - `ObsidianProvider` / `useObsidian`: context that configures the components.
18
+ - `PropertiesPanel`, `Properties`, `FormattedDate`: render a Note's Frontmatter properties; the panel remembers whether the reader left it open (`PAGE_PROPERTIES_KEY`, `PREVIEW_PROPERTIES_KEY`).
19
+ - `HoverPreview`, `PreviewBody`: open a preview of the Note an internal link points to, nested to any depth.
20
+ - `fetchPreviewPayload`, `slicePreviewHtml`, `previewPayloadUrl`, `PREVIEW_PATH` and the `PreviewPayload` type: the client side of the static Preview Payload described in [ADR 0009](../../docs/adr/0009-static-preview-payload.md).
21
+ - `Collapsible`, `CollapsiblePanel`, `CollapsibleTrigger`, `usePersistedOpen`: the building blocks.
22
+
23
+ See [`examples/fumadocs`](../../examples/fumadocs) for a complete usage, including styling.
24
+
25
+ ## License
26
+
27
+ MIT
@@ -0,0 +1,6 @@
1
+ import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible";
2
+ /** Base UI's Collapsible with stable `data-slot` hooks and no styles. */
3
+ export declare function Collapsible(props: CollapsiblePrimitive.Root.Props): import("react").JSX.Element;
4
+ export declare function CollapsibleTrigger(props: CollapsiblePrimitive.Trigger.Props): import("react").JSX.Element;
5
+ export declare function CollapsiblePanel(props: CollapsiblePrimitive.Panel.Props): import("react").JSX.Element;
6
+ //# sourceMappingURL=collapsible.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collapsible.d.ts","sourceRoot":"","sources":["../src/collapsible.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAEjF,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,KAAK,EAAE,oBAAoB,CAAC,IAAI,CAAC,KAAK,+BAEjE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,CAAC,OAAO,CAAC,KAAK,+BAI3E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,oBAAoB,CAAC,KAAK,CAAC,KAAK,+BAIvE"}
@@ -0,0 +1,14 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible";
4
+ /** Base UI's Collapsible with stable `data-slot` hooks and no styles. */
5
+ export function Collapsible(props) {
6
+ return _jsx(CollapsiblePrimitive.Root, { "data-slot": "collapsible", ...props });
7
+ }
8
+ export function CollapsibleTrigger(props) {
9
+ return (_jsx(CollapsiblePrimitive.Trigger, { "data-slot": "collapsible-trigger", ...props }));
10
+ }
11
+ export function CollapsiblePanel(props) {
12
+ return (_jsx(CollapsiblePrimitive.Panel, { "data-slot": "collapsible-content", ...props }));
13
+ }
14
+ //# sourceMappingURL=collapsible.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collapsible.js","sourceRoot":"","sources":["../src/collapsible.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAEjF,yEAAyE;AACzE,MAAM,UAAU,WAAW,CAAC,KAAsC;IAChE,OAAO,KAAC,oBAAoB,CAAC,IAAI,iBAAW,aAAa,KAAK,KAAK,GAAI,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAyC;IAC1E,OAAO,CACL,KAAC,oBAAoB,CAAC,OAAO,iBAAW,qBAAqB,KAAK,KAAK,GAAI,CAC5E,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAuC;IACtE,OAAO,CACL,KAAC,oBAAoB,CAAC,KAAK,iBAAW,qBAAqB,KAAK,KAAK,GAAI,CAC1E,CAAC;AACJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Numeric date through `Intl.DateTimeFormat` (e.g. `17/02/2026, 11:33` in
3
+ * pt-BR, `2/17/2026, 11:33 AM` in en-US). The server cannot know the visitor's
4
+ * locale, so the ISO string renders first and the formatted value swaps in
5
+ * after hydration: no hydration mismatch, no layout shift.
6
+ *
7
+ * `locales` defaults to the browser's locale; `options` replaces the default
8
+ * numeric format (a datetime still gets its time unless `options` says
9
+ * otherwise).
10
+ */
11
+ export declare function FormattedDate({ date, kind, locales, options, }: {
12
+ date: string;
13
+ kind: "date" | "datetime";
14
+ locales?: Intl.LocalesArgument;
15
+ options?: Intl.DateTimeFormatOptions;
16
+ }): import("react").JSX.Element;
17
+ //# sourceMappingURL=formatted-date.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatted-date.d.ts","sourceRoot":"","sources":["../src/formatted-date.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,OAAO,GACR,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAC1B,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC;IAC/B,OAAO,CAAC,EAAE,IAAI,CAAC,qBAAqB,CAAC;CACtC,+BA8BA"}
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ /**
5
+ * Numeric date through `Intl.DateTimeFormat` (e.g. `17/02/2026, 11:33` in
6
+ * pt-BR, `2/17/2026, 11:33 AM` in en-US). The server cannot know the visitor's
7
+ * locale, so the ISO string renders first and the formatted value swaps in
8
+ * after hydration: no hydration mismatch, no layout shift.
9
+ *
10
+ * `locales` defaults to the browser's locale; `options` replaces the default
11
+ * numeric format (a datetime still gets its time unless `options` says
12
+ * otherwise).
13
+ */
14
+ export function FormattedDate({ date, kind, locales, options, }) {
15
+ const [text, setText] = useState(date);
16
+ const optionsKey = JSON.stringify(options ?? null);
17
+ const localesKey = JSON.stringify(locales ?? null);
18
+ // biome-ignore lint/correctness/useExhaustiveDependencies: the keys stand for the objects
19
+ useEffect(() => {
20
+ // `T00:00` keeps a date-only value in the local day: `new Date("2025-02-03")`
21
+ // is UTC midnight and would render as the previous day west of UTC.
22
+ const parsed = kind === "date" ? new Date(`${date}T00:00`) : new Date(date);
23
+ setText(new Intl.DateTimeFormat(locales, options ?? {
24
+ year: "numeric",
25
+ month: "numeric",
26
+ day: "numeric",
27
+ ...(kind === "datetime"
28
+ ? { hour: "numeric", minute: "2-digit" }
29
+ : {}),
30
+ }).format(parsed));
31
+ }, [date, kind, localesKey, optionsKey]);
32
+ return (_jsx("time", { "data-property-date": kind, dateTime: date, children: text }));
33
+ }
34
+ //# sourceMappingURL=formatted-date.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatted-date.js","sourceRoot":"","sources":["../src/formatted-date.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,EAC5B,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,OAAO,GAMR;IACC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC;IAEnD,0FAA0F;IAC1F,SAAS,CAAC,GAAG,EAAE;QACb,8EAA8E;QAC9E,oEAAoE;QACpE,MAAM,MAAM,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5E,OAAO,CACL,IAAI,IAAI,CAAC,cAAc,CACrB,OAAO,EACP,OAAO,IAAI;YACT,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,SAAS;YAChB,GAAG,EAAE,SAAS;YACd,GAAG,CAAC,IAAI,KAAK,UAAU;gBACrB,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE;gBACxC,CAAC,CAAC,EAAE,CAAC;SACR,CACF,CAAC,MAAM,CAAC,MAAM,CAAC,CACjB,CAAC;IACJ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC;IAEzC,OAAO,CACL,qCAA0B,IAAI,EAAE,QAAQ,EAAE,IAAI,YAC3C,IAAI,GACA,CACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { type ComponentProps, type ReactNode } from "react";
2
+ /**
3
+ * An internal link that shows a Hover Preview of the Note it points at.
4
+ * `slug` is the target Note's Slug and `subpath` the link's Subpath (the part
5
+ * after `#`), which picks the part of the Note the preview shows.
6
+ */
7
+ export declare function HoverPreview({ href, slug, subpath, children, ...props }: Omit<ComponentProps<"a">, "href"> & {
8
+ href: string;
9
+ slug: string[];
10
+ subpath?: string | undefined;
11
+ children?: ReactNode;
12
+ }): import("react").JSX.Element;
13
+ //# sourceMappingURL=hover-preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hover-preview.d.ts","sourceRoot":"","sources":["../src/hover-preview.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,SAAS,EAOf,MAAM,OAAO,CAAC;AAmEf;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,+BAuGA"}
@@ -0,0 +1,107 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { PreviewCard } from "@base-ui/react/preview-card";
4
+ import { createContext, useCallback, useContext, useEffect, useRef, useState, useSyncExternalStore, } from "react";
5
+ import { PreviewBody } from "./preview-body.js";
6
+ import { fetchPreviewPayload } from "./preview-client.js";
7
+ import { slicePreviewHtml } from "./preview-slice.js";
8
+ import { PREVIEW_PROPERTIES_KEY } from "./properties-keys.js";
9
+ import { useObsidian } from "./provider.js";
10
+ import { slugKey } from "./slug-key.js";
11
+ /**
12
+ * Lets a preview tell the one that holds it (the link it sits in belongs to
13
+ * that preview's body) that it closed, so the parent can close too when the
14
+ * pointer left everything while the nested preview was keeping it open.
15
+ */
16
+ const ChildClosedContext = createContext(null);
17
+ const OPEN_DELAY = 300;
18
+ const CLOSE_DELAY = 150;
19
+ function PreviewContent({ payload, subpath, }) {
20
+ const html = slicePreviewHtml(payload.html, subpath);
21
+ const { renderProperties, previewBodyClassName } = useObsidian();
22
+ return (_jsxs("div", { "data-hover-preview-content": "", children: [renderProperties({
23
+ defaultOpen: false,
24
+ noFlash: false,
25
+ properties: payload.properties,
26
+ storageKey: PREVIEW_PROPERTIES_KEY,
27
+ }), _jsx("div", { className: previewBodyClassName || undefined, "data-hover-preview-body": "", children: _jsx(PreviewBody, { html: html }) })] }));
28
+ }
29
+ const HOVER_QUERY = "(hover: hover)";
30
+ function subscribeHover(onChange) {
31
+ const query = window.matchMedia(HOVER_QUERY);
32
+ query.addEventListener("change", onChange);
33
+ return () => query.removeEventListener("change", onChange);
34
+ }
35
+ /** Touch devices have no hover, so their links stay plain links. */
36
+ function useCanHover() {
37
+ return useSyncExternalStore(subscribeHover, () => window.matchMedia(HOVER_QUERY).matches, () => true);
38
+ }
39
+ /**
40
+ * An internal link that shows a Hover Preview of the Note it points at.
41
+ * `slug` is the target Note's Slug and `subpath` the link's Subpath (the part
42
+ * after `#`), which picks the part of the Note the preview shows.
43
+ */
44
+ export function HoverPreview({ href, slug, subpath, children, ...props }) {
45
+ const { linkComponent: Link, previewPayloadUrl } = useObsidian();
46
+ const canHover = useCanHover();
47
+ const [payload, setPayload] = useState(null);
48
+ const [wantOpen, setWantOpen] = useState(false);
49
+ const key = slugKey(slug);
50
+ const notifyParent = useContext(ChildClosedContext);
51
+ const isOpen = wantOpen && payload !== null;
52
+ // What the reader's pointer or focus is on: the link or the popup, by
53
+ // pointer or by focus. The popup's React events also cover the nested
54
+ // previews portaled out of it, so "inside the popup" means anywhere in the
55
+ // nested chain, for focus as well as for the pointer.
56
+ const presence = useRef(new Set());
57
+ const closeTimer = useRef(undefined);
58
+ const setPresence = (part, input, present) => {
59
+ const id = `${part}:${input}`;
60
+ if (present) {
61
+ presence.current.add(id);
62
+ clearTimeout(closeTimer.current);
63
+ }
64
+ else {
65
+ presence.current.delete(id);
66
+ }
67
+ };
68
+ const onChildClosed = useCallback(() => {
69
+ if (presence.current.size > 0)
70
+ return;
71
+ clearTimeout(closeTimer.current);
72
+ closeTimer.current = setTimeout(() => setWantOpen(false), CLOSE_DELAY);
73
+ }, []);
74
+ useEffect(() => () => clearTimeout(closeTimer.current), []);
75
+ const wasOpen = useRef(false);
76
+ useEffect(() => {
77
+ if (wasOpen.current && !isOpen)
78
+ notifyParent?.();
79
+ wasOpen.current = isOpen;
80
+ }, [isOpen, notifyParent]);
81
+ // Starts on pointer enter or focus, in parallel with the open delay. A
82
+ // failure opens nothing and is not cached, so the next hover retries.
83
+ // biome-ignore lint/correctness/useExhaustiveDependencies: `key` identifies `slug`
84
+ const load = useCallback(() => {
85
+ fetchPreviewPayload(slug, previewPayloadUrl).then(setPayload, () => setWantOpen(false));
86
+ }, [key, previewPayloadUrl]);
87
+ // Leaving before the content has arrived means the preview never opens.
88
+ const cancelPending = useCallback(() => {
89
+ if (!payload)
90
+ setWantOpen(false);
91
+ }, [payload]);
92
+ // Hovering or focusing the link starts loading its payload; leaving it
93
+ // before the payload has arrived cancels the preview.
94
+ const enter = (input) => {
95
+ setPresence("trigger", input, true);
96
+ load();
97
+ };
98
+ const leave = (input) => {
99
+ setPresence("trigger", input, false);
100
+ cancelPending();
101
+ };
102
+ if (!canHover) {
103
+ return (_jsx(Link, { href: href, ...props, children: children }));
104
+ }
105
+ return (_jsxs(PreviewCard.Root, { onOpenChange: (next) => setWantOpen(next), open: isOpen, children: [_jsx(PreviewCard.Trigger, { closeDelay: CLOSE_DELAY, delay: OPEN_DELAY, onBlur: () => leave("focus"), onFocus: () => enter("focus"), onPointerEnter: () => enter("pointer"), onPointerLeave: () => leave("pointer"), render: _jsx(Link, { href: href, ...props }), children: children }), _jsx(PreviewCard.Portal, { children: _jsx(PreviewCard.Positioner, { "data-hover-preview-positioner": "", sideOffset: 8, children: _jsx(PreviewCard.Popup, { "data-hover-preview": "", "data-testid": "hover-preview", onBlur: () => setPresence("popup", "focus", false), onFocus: () => setPresence("popup", "focus", true), onPointerEnter: () => setPresence("popup", "pointer", true), onPointerLeave: () => setPresence("popup", "pointer", false), children: _jsx(ChildClosedContext.Provider, { value: onChildClosed, children: payload && (_jsx(PreviewContent, { payload: payload, subpath: subpath })) }) }) }) })] }));
106
+ }
107
+ //# sourceMappingURL=hover-preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hover-preview.js","sourceRoot":"","sources":["../src/hover-preview.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export { Collapsible, CollapsiblePanel, CollapsibleTrigger, } from "./collapsible.js";
2
+ export { FormattedDate } from "./formatted-date.js";
3
+ export { HoverPreview } from "./hover-preview.js";
4
+ export { PreviewBody } from "./preview-body.js";
5
+ export { fetchPreviewPayload } from "./preview-client.js";
6
+ export { slicePreviewHtml } from "./preview-slice.js";
7
+ export { PREVIEW_PATH, PREVIEW_SLUG_ATTR, PREVIEW_SUBPATH_ATTR, type PreviewPayload, previewPayloadUrl, } from "./preview-types.js";
8
+ export { Properties, type PropertiesIcons } from "./properties.js";
9
+ export { PAGE_PROPERTIES_KEY, PREVIEW_PROPERTIES_KEY, } from "./properties-keys.js";
10
+ export { PropertiesPanel } from "./properties-panel.js";
11
+ export { type ObsidianContextValue, ObsidianProvider, type PreviewPropertiesProps, useObsidian, } from "./provider.js";
12
+ export { slugFromKey, slugKey } from "./slug-key.js";
13
+ export { persistedOpenScript, usePersistedOpen } from "./use-persisted-open.js";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,cAAc,EACnB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EACL,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EACL,KAAK,oBAAoB,EACzB,gBAAgB,EAChB,KAAK,sBAAsB,EAC3B,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ export { Collapsible, CollapsiblePanel, CollapsibleTrigger, } from "./collapsible.js";
2
+ export { FormattedDate } from "./formatted-date.js";
3
+ export { HoverPreview } from "./hover-preview.js";
4
+ export { PreviewBody } from "./preview-body.js";
5
+ export { fetchPreviewPayload } from "./preview-client.js";
6
+ export { slicePreviewHtml } from "./preview-slice.js";
7
+ export { PREVIEW_PATH, PREVIEW_SLUG_ATTR, PREVIEW_SUBPATH_ATTR, previewPayloadUrl, } from "./preview-types.js";
8
+ export { Properties } from "./properties.js";
9
+ export { PAGE_PROPERTIES_KEY, PREVIEW_PROPERTIES_KEY, } from "./properties-keys.js";
10
+ export { PropertiesPanel } from "./properties-panel.js";
11
+ export { ObsidianProvider, useObsidian, } from "./provider.js";
12
+ export { slugFromKey, slugKey } from "./slug-key.js";
13
+ export { persistedOpenScript, usePersistedOpen } from "./use-persisted-open.js";
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,EAEpB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAwB,MAAM,iBAAiB,CAAC;AACnE,OAAO,EACL,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAEL,gBAAgB,EAEhB,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Renders a Preview Payload's body (already cut to the link's Subpath) as
3
+ * React elements, so its links can be Hover Previews themselves.
4
+ */
5
+ export declare function PreviewBody({ html }: {
6
+ html: string;
7
+ }): JSX.Element;
8
+ //# sourceMappingURL=preview-body.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-body.d.ts","sourceRoot":"","sources":["../src/preview-body.tsx"],"names":[],"mappings":"AAiDA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,eAOrD"}
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { fromHtml } from "hast-util-from-html";
4
+ import { toJsxRuntime } from "hast-util-to-jsx-runtime";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ import { HoverPreview } from "./hover-preview.js";
7
+ import { PREVIEW_SLUG_ATTR, PREVIEW_SUBPATH_ATTR } from "./preview-types.js";
8
+ import { useObsidian } from "./provider.js";
9
+ import { slugFromKey } from "./slug-key.js";
10
+ /**
11
+ * A link in a preview's body. The payload builder marks the links that
12
+ * resolve to a Vault Note with the target's Slug, so those become a nested
13
+ * Hover Preview; every other link uses the provider's link component.
14
+ */
15
+ function PreviewLink({ [PREVIEW_SLUG_ATTR]: slug, [PREVIEW_SUBPATH_ATTR]: subpath, href, children, ...props }) {
16
+ const { linkComponent: Link } = useObsidian();
17
+ if (!slug || !href) {
18
+ return (_jsx(Link, { href: href, ...props, children: children }));
19
+ }
20
+ return (_jsx(HoverPreview, { ...props, href: href, slug: slugFromKey(slug), subpath: subpath, children: children }));
21
+ }
22
+ /**
23
+ * Renders a Preview Payload's body (already cut to the link's Subpath) as
24
+ * React elements, so its links can be Hover Previews themselves.
25
+ */
26
+ export function PreviewBody({ html }) {
27
+ return toJsxRuntime(fromHtml(html, { fragment: true }), {
28
+ Fragment,
29
+ jsx,
30
+ jsxs,
31
+ components: { a: PreviewLink },
32
+ });
33
+ }
34
+ //# sourceMappingURL=preview-body.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-body.js","sourceRoot":"","sources":["../src/preview-body.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAO5C;;;;GAIG;AACH,SAAS,WAAW,CAAC,EACnB,CAAC,iBAAiB,CAAC,EAAE,IAAI,EACzB,CAAC,oBAAoB,CAAC,EAAE,OAAO,EAC/B,IAAI,EACJ,QAAQ,EACR,GAAG,KAAK,EACI;IACZ,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,WAAW,EAAE,CAAC;IAC9C,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACnB,OAAO,CACL,KAAC,IAAI,IAAC,IAAI,EAAE,IAAc,KAAM,KAAK,YAClC,QAAQ,GACJ,CACR,CAAC;IACJ,CAAC;IACD,OAAO,CACL,KAAC,YAAY,OACP,KAAK,EACT,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,EACvB,OAAO,EAAE,OAAO,YAEf,QAAQ,GACI,CAChB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,EAAE,IAAI,EAAoB;IACpD,OAAO,YAAY,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE;QACtD,QAAQ;QACR,GAAG;QACH,IAAI;QACJ,UAAU,EAAE,EAAE,CAAC,EAAE,WAAW,EAAE;KAC/B,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { PreviewPayload } from "./preview-types.js";
2
+ /**
3
+ * Fetches the Preview Payload of the Note published under `slug` from the URL
4
+ * `payloadUrl` builds. The first call per Slug downloads it; later calls in the
5
+ * session, whatever Subpath the link carried, share that one request. A failed
6
+ * request is forgotten so a later hover can retry.
7
+ */
8
+ export declare function fetchPreviewPayload(slug: readonly string[], payloadUrl: (slug: readonly string[]) => string): Promise<PreviewPayload>;
9
+ //# sourceMappingURL=preview-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-client.d.ts","sourceRoot":"","sources":["../src/preview-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAKzD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,UAAU,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,GAC9C,OAAO,CAAC,cAAc,CAAC,CAgBzB"}
@@ -0,0 +1,27 @@
1
+ import { slugKey } from "./slug-key.js";
2
+ const cache = new Map();
3
+ /**
4
+ * Fetches the Preview Payload of the Note published under `slug` from the URL
5
+ * `payloadUrl` builds. The first call per Slug downloads it; later calls in the
6
+ * session, whatever Subpath the link carried, share that one request. A failed
7
+ * request is forgotten so a later hover can retry.
8
+ */
9
+ export function fetchPreviewPayload(slug, payloadUrl) {
10
+ const key = slugKey(slug);
11
+ const cached = cache.get(key);
12
+ if (cached)
13
+ return cached;
14
+ const request = fetch(payloadUrl(slug))
15
+ .then((response) => {
16
+ if (!response.ok)
17
+ throw new Error(`Preview failed: ${response.status}`);
18
+ return response.json();
19
+ })
20
+ .catch((error) => {
21
+ cache.delete(key);
22
+ throw error;
23
+ });
24
+ cache.set(key, request);
25
+ return request;
26
+ }
27
+ //# sourceMappingURL=preview-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-client.js","sourceRoot":"","sources":["../src/preview-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAmC,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAuB,EACvB,UAA+C;IAE/C,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;SACpC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;QACjB,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACxE,OAAO,QAAQ,CAAC,IAAI,EAA6B,CAAC;IACpD,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACf,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClB,MAAM,KAAK,CAAC;IACd,CAAC,CAAC,CAAC;IACL,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACxB,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Cuts the part of a Note's rendered body that a link's Subpath points at.
3
+ * `subpath` is the raw hash without `#`: a heading anchor (`alpha-heading`) or
4
+ * a Block ID (`^blockid`). A heading selects itself and everything up to the
5
+ * next heading of the same or higher level; a Block ID selects the one element
6
+ * carrying `id="^blockid"` (a list item stays inside its list). When there is
7
+ * no Subpath, or its anchor is not found, the whole Note comes back. Pure and
8
+ * DOM-free: the input string is never mutated.
9
+ */
10
+ export declare function slicePreviewHtml(html: string, subpath: string | undefined): string;
11
+ //# sourceMappingURL=preview-slice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-slice.d.ts","sourceRoot":"","sources":["../src/preview-slice.ts"],"names":[],"mappings":"AAwCA;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAAG,SAAS,GAC1B,MAAM,CAyBR"}
@@ -0,0 +1,67 @@
1
+ import { fromHtml } from "hast-util-from-html";
2
+ import { toHtml } from "hast-util-to-html";
3
+ const HEADING = /^h([1-6])$/;
4
+ function headingLevel(node) {
5
+ if (node.type !== "element")
6
+ return undefined;
7
+ const match = HEADING.exec(node.tagName);
8
+ return match ? Number(match[1]) : undefined;
9
+ }
10
+ function decode(subpath) {
11
+ try {
12
+ return decodeURIComponent(subpath);
13
+ }
14
+ catch {
15
+ return subpath;
16
+ }
17
+ }
18
+ /** Finds the element with the given id, with the list of siblings holding it. */
19
+ function findElementById(parent, id) {
20
+ const children = parent.children;
21
+ for (let index = 0; index < children.length; index++) {
22
+ const node = children[index];
23
+ if (node?.type !== "element")
24
+ continue;
25
+ if (node.properties.id === id)
26
+ return { siblings: children, index, parent };
27
+ const inner = findElementById(node, id);
28
+ if (inner)
29
+ return inner;
30
+ }
31
+ return null;
32
+ }
33
+ /**
34
+ * Cuts the part of a Note's rendered body that a link's Subpath points at.
35
+ * `subpath` is the raw hash without `#`: a heading anchor (`alpha-heading`) or
36
+ * a Block ID (`^blockid`). A heading selects itself and everything up to the
37
+ * next heading of the same or higher level; a Block ID selects the one element
38
+ * carrying `id="^blockid"` (a list item stays inside its list). When there is
39
+ * no Subpath, or its anchor is not found, the whole Note comes back. Pure and
40
+ * DOM-free: the input string is never mutated.
41
+ */
42
+ export function slicePreviewHtml(html, subpath) {
43
+ if (!subpath)
44
+ return html;
45
+ const tree = fromHtml(html, { fragment: true });
46
+ const anchor = decode(subpath);
47
+ const hit = findElementById(tree, anchor) ?? findElementById(tree, subpath);
48
+ if (!hit)
49
+ return html;
50
+ const node = hit.siblings[hit.index];
51
+ const level = headingLevel(node);
52
+ if (level !== undefined && !anchor.startsWith("^")) {
53
+ let end = hit.index + 1;
54
+ while (end < hit.siblings.length) {
55
+ const next = headingLevel(hit.siblings[end]);
56
+ if (next !== undefined && next <= level)
57
+ break;
58
+ end++;
59
+ }
60
+ return toHtml(hit.siblings.slice(hit.index, end));
61
+ }
62
+ if (node.tagName === "li" && hit.parent.type === "element") {
63
+ return toHtml({ ...hit.parent, children: [node] });
64
+ }
65
+ return toHtml(node);
66
+ }
67
+ //# sourceMappingURL=preview-slice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-slice.js","sourceRoot":"","sources":["../src/preview-slice.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /** One Note, prepared at build time for Hover Previews (see ADR 0001). */
2
+ export type PreviewPayload = {
3
+ title: string;
4
+ /** Structured data from `getFrontmatter`; the Properties Panel renders it. */
5
+ properties: unknown;
6
+ /** The Note's body as rendered HTML, without Frontmatter. */
7
+ html: string;
8
+ };
9
+ /**
10
+ * Attributes the payload builder puts on a link that can be previewed: the
11
+ * target Note's Slug (as a `slugKey`) and the link's Subpath.
12
+ */
13
+ export declare const PREVIEW_SLUG_ATTR = "data-preview-slug";
14
+ export declare const PREVIEW_SUBPATH_ATTR = "data-preview-subpath";
15
+ /** Where Preview Payloads are served from. */
16
+ export declare const PREVIEW_PATH = "/api/preview/";
17
+ /** URL of the Preview Payload for the Note published under `slug`. */
18
+ export declare function previewPayloadUrl(slug: readonly string[]): string;
19
+ //# sourceMappingURL=preview-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-types.d.ts","sourceRoot":"","sources":["../src/preview-types.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,UAAU,EAAE,OAAO,CAAC;IACpB,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,sBAAsB,CAAC;AACrD,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAE3D,8CAA8C;AAC9C,eAAO,MAAM,YAAY,kBAAkB,CAAC;AAE5C,sEAAsE;AACtE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAEjE"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Attributes the payload builder puts on a link that can be previewed: the
3
+ * target Note's Slug (as a `slugKey`) and the link's Subpath.
4
+ */
5
+ export const PREVIEW_SLUG_ATTR = "data-preview-slug";
6
+ export const PREVIEW_SUBPATH_ATTR = "data-preview-subpath";
7
+ /** Where Preview Payloads are served from. */
8
+ export const PREVIEW_PATH = "/api/preview/";
9
+ /** URL of the Preview Payload for the Note published under `slug`. */
10
+ export function previewPayloadUrl(slug) {
11
+ return `${PREVIEW_PATH}${slug.map(encodeURIComponent).join("/")}`;
12
+ }
13
+ //# sourceMappingURL=preview-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-types.js","sourceRoot":"","sources":["../src/preview-types.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAE3D,8CAA8C;AAC9C,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC;AAE5C,sEAAsE;AACtE,MAAM,UAAU,iBAAiB,CAAC,IAAuB;IACvD,OAAO,GAAG,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AACpE,CAAC"}
@@ -0,0 +1,4 @@
1
+ /** localStorage keys remembering the Properties Panel state; one per surface. */
2
+ export declare const PAGE_PROPERTIES_KEY = "properties-panel:page";
3
+ export declare const PREVIEW_PROPERTIES_KEY = "properties-panel:preview";
4
+ //# sourceMappingURL=properties-keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"properties-keys.d.ts","sourceRoot":"","sources":["../src/properties-keys.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,0BAA0B,CAAC;AAC3D,eAAO,MAAM,sBAAsB,6BAA6B,CAAC"}
@@ -0,0 +1,4 @@
1
+ /** localStorage keys remembering the Properties Panel state; one per surface. */
2
+ export const PAGE_PROPERTIES_KEY = "properties-panel:page";
3
+ export const PREVIEW_PROPERTIES_KEY = "properties-panel:preview";
4
+ //# sourceMappingURL=properties-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"properties-keys.js","sourceRoot":"","sources":["../src/properties-keys.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,CAAC,MAAM,mBAAmB,GAAG,uBAAuB,CAAC;AAC3D,MAAM,CAAC,MAAM,sBAAsB,GAAG,0BAA0B,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { type ReactNode } from "react";
2
+ /**
3
+ * Collapsible shell around the Properties rows. Remembers its state in
4
+ * `localStorage` under `storageKey`; each surface (page, preview) uses its own.
5
+ *
6
+ * The root carries `data-open`, which the inline no-flash script sets before
7
+ * React has hydrated. The panel stays mounted when closed, so style the closed
8
+ * state from `[data-properties-panel][data-open="false"]` (for example
9
+ * `display: none` on `[data-properties-content]`).
10
+ */
11
+ export declare function PropertiesPanel({ children, storageKey, defaultOpen, noFlash, label, expandIcon, }: {
12
+ children: ReactNode;
13
+ storageKey: string;
14
+ defaultOpen: boolean;
15
+ /** Skip the inline script where the panel is only rendered on the client. */
16
+ noFlash?: boolean;
17
+ /** The trigger's text. */
18
+ label?: ReactNode;
19
+ /** Icon slot for the expand/collapse control; there is no default. */
20
+ expandIcon?: ReactNode;
21
+ }): import("react").JSX.Element;
22
+ //# sourceMappingURL=properties-panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"properties-panel.d.ts","sourceRoot":"","sources":["../src/properties-panel.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAS,MAAM,OAAO,CAAC;AAS9C;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,UAAU,EACV,WAAW,EACX,OAAc,EACd,KAAoB,EACpB,UAAU,GACX,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0BAA0B;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,sEAAsE;IACtE,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB,+BAoCA"}
@@ -0,0 +1,24 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useId } from "react";
4
+ import { Collapsible, CollapsiblePanel, CollapsibleTrigger, } from "./collapsible.js";
5
+ import { persistedOpenScript, usePersistedOpen } from "./use-persisted-open.js";
6
+ /**
7
+ * Collapsible shell around the Properties rows. Remembers its state in
8
+ * `localStorage` under `storageKey`; each surface (page, preview) uses its own.
9
+ *
10
+ * The root carries `data-open`, which the inline no-flash script sets before
11
+ * React has hydrated. The panel stays mounted when closed, so style the closed
12
+ * state from `[data-properties-panel][data-open="false"]` (for example
13
+ * `display: none` on `[data-properties-content]`).
14
+ */
15
+ export function PropertiesPanel({ children, storageKey, defaultOpen, noFlash = true, label = "Properties", expandIcon, }) {
16
+ const id = useId();
17
+ const [open, setOpen] = usePersistedOpen(storageKey, defaultOpen);
18
+ return (_jsxs(_Fragment, { children: [_jsx("div", { "data-open": open, "data-properties-panel": "", id: id, suppressHydrationWarning: true, children: _jsxs(Collapsible, { onOpenChange: setOpen, open: open, children: [_jsxs(CollapsibleTrigger, { "data-properties-trigger": "", children: [expandIcon ? (_jsx("span", { "aria-hidden": true, "data-properties-expand-icon": "", children: expandIcon })) : null, label] }), _jsx(CollapsiblePanel, { "data-properties-content": "", keepMounted: true, children: children })] }) }), noFlash ? (_jsx("script", {
19
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: no-flash script, content is built from constants
20
+ dangerouslySetInnerHTML: {
21
+ __html: persistedOpenScript(id, storageKey),
22
+ } })) : null] }));
23
+ }
24
+ //# sourceMappingURL=properties-panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"properties-panel.js","sourceRoot":"","sources":["../src/properties-panel.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAkB,KAAK,EAAE,MAAM,OAAO,CAAC;AAE9C,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhF;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,QAAQ,EACR,UAAU,EACV,WAAW,EACX,OAAO,GAAG,IAAI,EACd,KAAK,GAAG,YAAY,EACpB,UAAU,GAWX;IACC,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,gBAAgB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAElE,OAAO,CACL,8BACE,2BACa,IAAI,2BACO,EAAE,EACxB,EAAE,EAAE,EAAE,EACN,wBAAwB,kBAExB,MAAC,WAAW,IAAC,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,aAC5C,MAAC,kBAAkB,+BAAyB,EAAE,aAC3C,UAAU,CAAC,CAAC,CAAC,CACZ,mEAA8C,EAAE,YAC7C,UAAU,GACN,CACR,CAAC,CAAC,CAAC,IAAI,EACP,KAAK,IACa,EACrB,KAAC,gBAAgB,+BAAyB,EAAE,EAAC,WAAW,kBACrD,QAAQ,GACQ,IACP,GACV,EACL,OAAO,CAAC,CAAC,CAAC,CACT;gBACE,yGAAyG;gBACzG,uBAAuB,EAAE;oBACvB,MAAM,EAAE,mBAAmB,CAAC,EAAE,EAAE,UAAU,CAAC;iBAC5C,GACD,CACH,CAAC,CAAC,CAAC,IAAI,IACP,CACJ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { ComponentProps, ComponentType, ReactNode } from "react";
2
+ import type { FrontmatterDate, PropertyType } from "remark-obsidian-md";
3
+ /** The property types the panel infers, mirroring Obsidian's. */
4
+ type InferredType = Extract<PropertyType, "text" | "multitext" | "number" | "checkbox" | "date" | "datetime" | "tags" | "aliases">;
5
+ /**
6
+ * Icon slots: one per property type, `unknown` for the rest, `expand` for the
7
+ * expand/collapse control and `checked` for a checked checkbox.
8
+ */
9
+ export type PropertiesIcons = Partial<Record<InferredType | "unknown" | "expand" | "checked", ReactNode>>;
10
+ type LinkComponent = ComponentType<ComponentProps<"a"> & {
11
+ href: string;
12
+ }>;
13
+ /**
14
+ * The Properties Panel: a collapsible list of a Note's Frontmatter, as
15
+ * produced by `getFrontmatter`. Headless: it emits `data-properties-*` and
16
+ * `data-property-*` attributes and no classes, so all styling comes from your
17
+ * CSS, and icons arrive through the `icons` slots.
18
+ */
19
+ export declare function Properties({ properties, storageKey, defaultOpen, noFlash, hiddenKeys, icons, label, linkComponent, dateLocales, dateOptions, renderDate, }: {
20
+ properties: unknown;
21
+ /** localStorage key remembering the open state; one per surface. */
22
+ storageKey?: string;
23
+ defaultOpen?: boolean;
24
+ /** Emit the inline script that applies the stored state before hydration. */
25
+ noFlash?: boolean;
26
+ /** Frontmatter keys to leave out (for example ones a framework consumes). */
27
+ hiddenKeys?: readonly string[];
28
+ /** Icon slots per property type, plus `expand`; none by default. */
29
+ icons?: PropertiesIcons;
30
+ /** The panel trigger's text; defaults to "Properties". */
31
+ label?: ReactNode;
32
+ /** Component for Vault-internal links; defaults to a plain `<a>`. */
33
+ linkComponent?: LinkComponent | "a";
34
+ /** `Intl` locales for dates; defaults to the browser's. */
35
+ dateLocales?: Intl.LocalesArgument;
36
+ /** `Intl.DateTimeFormat` options for dates; defaults to a numeric format. */
37
+ dateOptions?: Intl.DateTimeFormatOptions;
38
+ /** Replaces the date rendering entirely. */
39
+ renderDate?: (value: FrontmatterDate) => ReactNode;
40
+ }): import("react").JSX.Element | null;
41
+ export {};
42
+ //# sourceMappingURL=properties.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"properties.d.ts","sourceRoot":"","sources":["../src/properties.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACtE,OAAO,KAAK,EACV,eAAe,EAEf,YAAY,EACb,MAAM,oBAAoB,CAAC;AAM5B,iEAAiE;AACjE,KAAK,YAAY,GAAG,OAAO,CACzB,YAAY,EACV,MAAM,GACN,WAAW,GACX,QAAQ,GACR,UAAU,GACV,MAAM,GACN,UAAU,GACV,MAAM,GACN,SAAS,CACZ,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,CACnC,MAAM,CAAC,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,EAAE,SAAS,CAAC,CACnE,CAAC;AAsEF,KAAK,aAAa,GAAG,aAAa,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AA2F3E;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,UAAU,EACV,UAAgC,EAChC,WAAkB,EAClB,OAAc,EACd,UAAU,EACV,KAAK,EACL,KAAK,EACL,aAAmB,EACnB,WAAW,EACX,WAAW,EACX,UAAU,GACX,EAAE;IACD,UAAU,EAAE,OAAO,CAAC;IACpB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,oEAAoE;IACpE,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,qEAAqE;IACrE,aAAa,CAAC,EAAE,aAAa,GAAG,GAAG,CAAC;IACpC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC;IACnC,6EAA6E;IAC7E,WAAW,CAAC,EAAE,IAAI,CAAC,qBAAqB,CAAC;IACzC,4CAA4C;IAC5C,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,SAAS,CAAC;CACpD,sCAqEA"}
@@ -0,0 +1,137 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { FormattedDate } from "./formatted-date.js";
3
+ import { PAGE_PROPERTIES_KEY } from "./properties-keys.js";
4
+ import { PropertiesPanel } from "./properties-panel.js";
5
+ /** Reserved frontmatter keys Obsidian types itself (case-insensitive). */
6
+ const RESERVED_TYPES = {
7
+ aliases: "aliases",
8
+ cssclasses: "multitext",
9
+ tags: "tags",
10
+ };
11
+ function isLink(value) {
12
+ return typeof value === "object" && value !== null && "label" in value;
13
+ }
14
+ function isDate(value) {
15
+ return (typeof value === "object" &&
16
+ value !== null &&
17
+ "date" in value &&
18
+ "kind" in value);
19
+ }
20
+ /** An empty frontmatter value (`Etiquetas:` → null, `tags: []`, blank text). */
21
+ function isEmpty(value) {
22
+ return (value === null ||
23
+ value === undefined ||
24
+ (typeof value === "string" && value.trim() === "") ||
25
+ (Array.isArray(value) && value.length === 0));
26
+ }
27
+ /** Plain nested objects are rare in vaults; render them flat as `key: value`. */
28
+ function toText(value) {
29
+ if (isLink(value))
30
+ return value.label;
31
+ if (isDate(value))
32
+ return value.date;
33
+ if (Array.isArray(value))
34
+ return value.map((item) => toText(item)).join(", ");
35
+ if (typeof value === "object" && value !== null) {
36
+ return Object.entries(value)
37
+ .map(([key, item]) => `${key}: ${toText(item)}`)
38
+ .join(", ");
39
+ }
40
+ return String(value);
41
+ }
42
+ /**
43
+ * Mirrors Obsidian's property type inference: reserved keys first, then the
44
+ * value's shape. Declared types from `.obsidian/types.json` are intentionally
45
+ * not consulted.
46
+ */
47
+ function inferType(key, value) {
48
+ const reserved = RESERVED_TYPES[key.toLowerCase()];
49
+ if (reserved)
50
+ return reserved;
51
+ if (value === null || value === undefined)
52
+ return "text";
53
+ if (Array.isArray(value))
54
+ return "multitext";
55
+ if (typeof value === "boolean")
56
+ return "checkbox";
57
+ if (typeof value === "number")
58
+ return "number";
59
+ if (isDate(value))
60
+ return value.kind;
61
+ if (isLink(value) || typeof value === "string")
62
+ return "text";
63
+ return "unknown";
64
+ }
65
+ /** Obsidian shows tags with a leading `#`, even when the YAML omits it. */
66
+ function tagLabel(value) {
67
+ const text = isLink(value) ? value.label : toText(value);
68
+ return `#${text.replace(/^#/, "")}`;
69
+ }
70
+ function Value({ value, type, options, }) {
71
+ if (value === null || value === undefined)
72
+ return null;
73
+ if (Array.isArray(value)) {
74
+ return value
75
+ .filter((item) => item !== null && item !== undefined)
76
+ .map((item, index) => (
77
+ // biome-ignore lint/suspicious/noArrayIndexKey: build-time static values with no stable identity
78
+ _jsx("span", { "data-properties-item": "", children: _jsx(Value, { options: options, type: type, value: item }) }, index)));
79
+ }
80
+ if (isLink(value)) {
81
+ const isTag = type === "tags";
82
+ const label = isTag ? tagLabel(value) : value.label;
83
+ const marks = {
84
+ ...(isTag ? { "data-property-tag": "" } : {}),
85
+ ...("unresolved" in value ? { "data-unresolved": "" } : {}),
86
+ };
87
+ if (value.url === undefined) {
88
+ return (_jsx("span", { "data-property-link": "unresolved", ...marks, children: label }));
89
+ }
90
+ if (value.url.startsWith("/")) {
91
+ const Link = options.linkComponent;
92
+ return (_jsx(Link, { "data-property-link": "internal", href: value.url, ...marks, children: label }));
93
+ }
94
+ return (_jsx("a", { "data-property-link": "external", href: value.url, rel: "noreferrer", target: "_blank", ...marks, children: label }));
95
+ }
96
+ if (isDate(value))
97
+ return options.renderDate(value);
98
+ if (typeof value === "boolean") {
99
+ return (_jsx("span", { "aria-label": value ? "true" : "false", "data-checked": value, "data-property-checkbox": "", role: "img", children: value ? options.checkedIcon : null }));
100
+ }
101
+ if (type === "tags") {
102
+ return _jsx("span", { "data-property-tag": "", children: tagLabel(value) });
103
+ }
104
+ if (typeof value === "number") {
105
+ return _jsx("span", { "data-property-number": "", children: toText(value) });
106
+ }
107
+ return _jsx(_Fragment, { children: toText(value) });
108
+ }
109
+ /**
110
+ * The Properties Panel: a collapsible list of a Note's Frontmatter, as
111
+ * produced by `getFrontmatter`. Headless: it emits `data-properties-*` and
112
+ * `data-property-*` attributes and no classes, so all styling comes from your
113
+ * CSS, and icons arrive through the `icons` slots.
114
+ */
115
+ export function Properties({ properties, storageKey = PAGE_PROPERTIES_KEY, defaultOpen = true, noFlash = true, hiddenKeys, icons, label, linkComponent = "a", dateLocales, dateOptions, renderDate, }) {
116
+ if (typeof properties !== "object" ||
117
+ properties === null ||
118
+ Array.isArray(properties)) {
119
+ return null;
120
+ }
121
+ const hidden = new Set(hiddenKeys);
122
+ const rows = Object.entries(properties).filter(([key, value]) => !hidden.has(key) && !isEmpty(value));
123
+ if (rows.length === 0)
124
+ return null;
125
+ const options = {
126
+ linkComponent,
127
+ checkedIcon: icons?.checked,
128
+ renderDate: renderDate ??
129
+ ((value) => (_jsx(FormattedDate, { date: value.date, kind: value.kind, ...(dateLocales !== undefined ? { locales: dateLocales } : {}), ...(dateOptions !== undefined ? { options: dateOptions } : {}) }))),
130
+ };
131
+ return (_jsx(PropertiesPanel, { defaultOpen: defaultOpen, expandIcon: icons?.expand, label: label, noFlash: noFlash, storageKey: storageKey, children: _jsx("div", { "data-properties-list": "", children: rows.map(([key, value]) => {
132
+ const type = inferType(key, value);
133
+ const icon = icons?.[type];
134
+ return (_jsxs("div", { "data-properties-row": "", "data-property-key": key, "data-property-type": type, children: [_jsxs("div", { "data-properties-key": "", children: [icon ? (_jsx("span", { "aria-hidden": true, "data-properties-type-icon": "", children: icon })) : null, _jsx("span", { "data-properties-name": "", title: key, children: key })] }), _jsx("div", { "data-properties-value": "", children: _jsx(Value, { options: options, type: type, value: value }) })] }, key));
135
+ }) }) }));
136
+ }
137
+ //# sourceMappingURL=properties.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"properties.js","sourceRoot":"","sources":["../src/properties.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { type ComponentProps, type ComponentType, type ReactNode } from "react";
2
+ type LinkComponent = ComponentType<ComponentProps<"a"> & {
3
+ href: string;
4
+ }>;
5
+ /** What a Hover Preview hands over to render a Note's Properties Panel. */
6
+ export type PreviewPropertiesProps = {
7
+ properties: unknown;
8
+ storageKey: string;
9
+ defaultOpen: boolean;
10
+ noFlash: boolean;
11
+ };
12
+ export type ObsidianContextValue = {
13
+ /** The component every link in a preview renders; a plain `<a>` by default. */
14
+ linkComponent?: LinkComponent | "a";
15
+ /** Builds the URL a Note's Preview Payload is fetched from. */
16
+ previewPayloadUrl?: (slug: readonly string[]) => string;
17
+ /**
18
+ * Class for the container of a preview's body, for stylesheets scoped to a
19
+ * Rendered Container (such as `markdown-rendered`). None by default.
20
+ */
21
+ previewBodyClassName?: string;
22
+ /** Renders the Properties Panel in a preview (for icons, hidden keys). */
23
+ renderProperties?: (props: PreviewPropertiesProps) => ReactNode;
24
+ };
25
+ /**
26
+ * Supplies framework-specific pieces to Hover Previews, however deeply nested:
27
+ * the link component, the Preview Payload URL and the Properties Panel.
28
+ */
29
+ export declare function ObsidianProvider({ children, ...value }: ObsidianContextValue & {
30
+ children?: ReactNode;
31
+ }): import("react").JSX.Element;
32
+ export declare function useObsidian(): Required<ObsidianContextValue>;
33
+ export {};
34
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,aAAa,EAElB,KAAK,SAAS,EAEf,MAAM,OAAO,CAAC;AAIf,KAAK,aAAa,GAAG,aAAa,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE3E,2EAA2E;AAC3E,MAAM,MAAM,sBAAsB,GAAG;IACnC,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,+EAA+E;IAC/E,aAAa,CAAC,EAAE,aAAa,GAAG,GAAG,CAAC;IACpC,+DAA+D;IAC/D,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC;IACxD;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,SAAS,CAAC;CACjE,CAAC;AAIF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,+BAMjD;AAED,wBAAgB,WAAW,IAAI,QAAQ,CAAC,oBAAoB,CAAC,CAY5D"}
@@ -0,0 +1,24 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useContext, } from "react";
4
+ import { previewPayloadUrl } from "./preview-types.js";
5
+ import { Properties } from "./properties.js";
6
+ const ObsidianContext = createContext({});
7
+ /**
8
+ * Supplies framework-specific pieces to Hover Previews, however deeply nested:
9
+ * the link component, the Preview Payload URL and the Properties Panel.
10
+ */
11
+ export function ObsidianProvider({ children, ...value }) {
12
+ return (_jsx(ObsidianContext.Provider, { value: value, children: children }));
13
+ }
14
+ export function useObsidian() {
15
+ const value = useContext(ObsidianContext);
16
+ return {
17
+ linkComponent: value.linkComponent ?? "a",
18
+ previewPayloadUrl: value.previewPayloadUrl ?? previewPayloadUrl,
19
+ previewBodyClassName: value.previewBodyClassName ?? "",
20
+ renderProperties: value.renderProperties ??
21
+ ((props) => (_jsx(Properties, { ...props, linkComponent: value.linkComponent ?? "a" }))),
22
+ };
23
+ }
24
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAGL,aAAa,EAEb,UAAU,GACX,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA0B7C,MAAM,eAAe,GAAG,aAAa,CAAuB,EAAE,CAAC,CAAC;AAEhE;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,QAAQ,EACR,GAAG,KAAK,EACwC;IAChD,OAAO,CACL,KAAC,eAAe,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YACnC,QAAQ,GACgB,CAC5B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,MAAM,KAAK,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC;IAC1C,OAAO;QACL,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,GAAG;QACzC,iBAAiB,EAAE,KAAK,CAAC,iBAAiB,IAAI,iBAAiB;QAC/D,oBAAoB,EAAE,KAAK,CAAC,oBAAoB,IAAI,EAAE;QACtD,gBAAgB,EACd,KAAK,CAAC,gBAAgB;YACtB,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACV,KAAC,UAAU,OAAK,KAAK,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,GAAG,GAAI,CACrE,CAAC;KACL,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ /** A Slug as one string, for cache keys and HTML attributes. */
2
+ export declare function slugKey(slug: readonly string[]): string;
3
+ /** The Slug a `slugKey` was made from. */
4
+ export declare function slugFromKey(key: string): string[];
5
+ //# sourceMappingURL=slug-key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug-key.d.ts","sourceRoot":"","sources":["../src/slug-key.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,wBAAgB,OAAO,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAEvD;AAED,0CAA0C;AAC1C,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAEjD"}
@@ -0,0 +1,9 @@
1
+ /** A Slug as one string, for cache keys and HTML attributes. */
2
+ export function slugKey(slug) {
3
+ return slug.join("/");
4
+ }
5
+ /** The Slug a `slugKey` was made from. */
6
+ export function slugFromKey(key) {
7
+ return key.split("/");
8
+ }
9
+ //# sourceMappingURL=slug-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug-key.js","sourceRoot":"","sources":["../src/slug-key.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,MAAM,UAAU,OAAO,CAAC,IAAuB;IAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Open/closed state remembered in `localStorage` under `key` and kept in sync
3
+ * between tabs (`storage` event) and between components using the same key.
4
+ *
5
+ * The server and the hydration pass render `defaultOpen`; the stored value
6
+ * takes over right after. Pair it with `persistedOpenScript` so the DOM already
7
+ * shows the stored state before hydration.
8
+ *
9
+ * @returns `[open, setOpen]`
10
+ */
11
+ export declare function usePersistedOpen(key: string, defaultOpen: boolean): readonly [boolean, (open: boolean) => void];
12
+ /**
13
+ * Inline script body for the no-flash guide: sets `data-open` on the element
14
+ * with `id` from the stored value, synchronously during HTML parsing.
15
+ */
16
+ export declare function persistedOpenScript(id: string, key: string): string;
17
+ //# sourceMappingURL=use-persisted-open.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-persisted-open.d.ts","sourceRoot":"","sources":["../src/use-persisted-open.ts"],"names":[],"mappings":"AA8BA;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,OAAO,GACnB,SAAS,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,CAoB7C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnE"}
@@ -0,0 +1,58 @@
1
+ "use client";
2
+ import { useCallback, useSyncExternalStore } from "react";
3
+ const OPEN = "1";
4
+ const CLOSED = "0";
5
+ /** Same-tab writes do not fire `storage`, so they announce themselves here. */
6
+ const LOCAL_EVENT = "persisted-open";
7
+ function read(key, defaultOpen) {
8
+ try {
9
+ const stored = window.localStorage.getItem(key);
10
+ if (stored === OPEN)
11
+ return true;
12
+ if (stored === CLOSED)
13
+ return false;
14
+ }
15
+ catch {
16
+ // localStorage can be blocked; fall through to the default.
17
+ }
18
+ return defaultOpen;
19
+ }
20
+ function subscribe(onChange) {
21
+ window.addEventListener("storage", onChange);
22
+ window.addEventListener(LOCAL_EVENT, onChange);
23
+ return () => {
24
+ window.removeEventListener("storage", onChange);
25
+ window.removeEventListener(LOCAL_EVENT, onChange);
26
+ };
27
+ }
28
+ /**
29
+ * Open/closed state remembered in `localStorage` under `key` and kept in sync
30
+ * between tabs (`storage` event) and between components using the same key.
31
+ *
32
+ * The server and the hydration pass render `defaultOpen`; the stored value
33
+ * takes over right after. Pair it with `persistedOpenScript` so the DOM already
34
+ * shows the stored state before hydration.
35
+ *
36
+ * @returns `[open, setOpen]`
37
+ */
38
+ export function usePersistedOpen(key, defaultOpen) {
39
+ const open = useSyncExternalStore(subscribe, () => read(key, defaultOpen), () => defaultOpen);
40
+ const setOpen = useCallback((next) => {
41
+ try {
42
+ window.localStorage.setItem(key, next ? OPEN : CLOSED);
43
+ }
44
+ catch {
45
+ // Not persisted; still notify so the UI reflects the choice.
46
+ }
47
+ window.dispatchEvent(new Event(LOCAL_EVENT));
48
+ }, [key]);
49
+ return [open, setOpen];
50
+ }
51
+ /**
52
+ * Inline script body for the no-flash guide: sets `data-open` on the element
53
+ * with `id` from the stored value, synchronously during HTML parsing.
54
+ */
55
+ export function persistedOpenScript(id, key) {
56
+ return `(function(){try{var v=localStorage.getItem(${JSON.stringify(key)});var e=document.getElementById(${JSON.stringify(id)});if(e&&(v==="1"||v==="0"))e.setAttribute("data-open",v==="1"?"true":"false")}catch(e){}})()`;
57
+ }
58
+ //# sourceMappingURL=use-persisted-open.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-persisted-open.js","sourceRoot":"","sources":["../src/use-persisted-open.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE1D,MAAM,IAAI,GAAG,GAAG,CAAC;AACjB,MAAM,MAAM,GAAG,GAAG,CAAC;AAEnB,+EAA+E;AAC/E,MAAM,WAAW,GAAG,gBAAgB,CAAC;AAErC,SAAS,IAAI,CAAC,GAAW,EAAE,WAAoB;IAC7C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChD,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACjC,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO,KAAK,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,4DAA4D;IAC9D,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,SAAS,SAAS,CAAC,QAAoB;IACrC,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC7C,MAAM,CAAC,gBAAgB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAC/C,OAAO,GAAG,EAAE;QACV,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,mBAAmB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACpD,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAC9B,GAAW,EACX,WAAoB;IAEpB,MAAM,IAAI,GAAG,oBAAoB,CAC/B,SAAS,EACT,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAC5B,GAAG,EAAE,CAAC,WAAW,CAClB,CAAC;IAEF,MAAM,OAAO,GAAG,WAAW,CACzB,CAAC,IAAa,EAAE,EAAE;QAChB,IAAI,CAAC;YACH,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC;YACP,6DAA6D;QAC/D,CAAC;QACD,MAAM,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;IAC/C,CAAC,EACD,CAAC,GAAG,CAAC,CACN,CAAC;IAEF,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAU,EAAE,GAAW;IACzD,OAAO,8CAA8C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,mCAAmC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,8FAA8F,CAAC;AAC9N,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,71 @@
1
1
  {
2
2
  "name": "react-obsidian-md",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "author": "Adriano Araújo (adrianoaraujodsantos@hotmail.com)",
5
+ "description": "Headless React components for Obsidian.md content: a collapsible Properties Panel with persisted state, styled entirely by your own CSS",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "react",
9
+ "headless",
10
+ "obsidian",
11
+ "properties",
12
+ "frontmatter",
13
+ "digital-garden",
14
+ "remark-obsidian-md"
15
+ ],
16
+ "homepage": "https://github.com/adrianoaraujods/obsidian-md/tree/main/packages/react-obsidian-md#readme",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/adrianoaraujods/obsidian-md.git",
20
+ "directory": "packages/react-obsidian-md"
21
+ },
22
+ "type": "module",
23
+ "main": "dist/index.js",
24
+ "module": "dist/index.js",
25
+ "types": "dist/index.d.ts",
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "default": "./dist/index.js"
33
+ }
34
+ },
35
+ "dependencies": {
36
+ "@base-ui/react": "^1.9.0",
37
+ "hast-util-from-html": "2.0.3",
38
+ "hast-util-to-html": "9.0.5",
39
+ "hast-util-to-jsx-runtime": "2.3.6"
40
+ },
41
+ "peerDependencies": {
42
+ "react": "^19.0.0",
43
+ "react-dom": "^19.0.0",
44
+ "remark-obsidian-md": "^3.0.0"
45
+ },
46
+ "devDependencies": {
47
+ "@types/hast": "^3.0.5",
48
+ "@types/node": "^25.2.0",
49
+ "@types/react": "^19",
50
+ "@types/react-dom": "^19",
51
+ "jsdom": "^27.0.0",
52
+ "react": "19.2.3",
53
+ "react-dom": "19.2.3",
54
+ "remark-obsidian-md": "3.0.0",
55
+ "typescript": "^5.9.3",
56
+ "vitest": "^5.0.5"
57
+ },
58
+ "sideEffects": false,
59
+ "bugs": {
60
+ "url": "https://github.com/adrianoaraujods/obsidian-md/issues"
61
+ },
62
+ "engines": {
63
+ "node": ">=20"
64
+ },
65
+ "scripts": {
66
+ "build": "tsc",
67
+ "test": "vitest run",
68
+ "lint": "biome check",
69
+ "format": "biome format --write"
70
+ }
6
71
  }