@type32/codemirror-rich-obsidian-editor 0.0.24 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@type32/codemirror-rich-obsidian-editor",
3
3
  "configKey": "cmOfmEditor",
4
- "version": "0.0.24",
4
+ "version": "0.0.25",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -27,30 +27,31 @@ function internalLinkSource(context) {
27
27
  const linkMap = context.state.facet(internalLinkMapFacet);
28
28
  const nameCounts = /* @__PURE__ */ new Map();
29
29
  linkMap.forEach((link) => {
30
- nameCounts.set(link.internalLinkName, (nameCounts.get(link.internalLinkName) || 0) + 1);
30
+ nameCounts.set(link.name, (nameCounts.get(link.name) || 0) + 1);
31
31
  });
32
32
  const searchString = textBefore.toLowerCase();
33
33
  const options = linkMap.filter(
34
- (link) => link.internalLinkName.toLowerCase().includes(searchString) || link.filePath && link.filePath.toLowerCase().includes(searchString)
34
+ (link) => link.name && link.name.toLowerCase().includes(searchString) || link.filePath && link.filePath.toLowerCase().includes(searchString)
35
35
  ).map((link) => {
36
- const isDuplicate = (nameCounts.get(link.internalLinkName) || 0) > 1;
36
+ if (!link.name) return null;
37
+ const isDuplicate = (nameCounts.get(link.name) || 0) > 1;
37
38
  if (isDuplicate) {
38
39
  return {
39
- label: link.filePath || link.internalLinkName,
40
- detail: link.internalLinkName,
41
- apply: link.filePath || link.internalLinkName
40
+ label: link.name,
41
+ detail: link.filePath,
42
+ apply: link.filePath || link.name
42
43
  };
43
44
  }
44
45
  return {
45
- label: link.internalLinkName,
46
+ label: link.name,
46
47
  detail: link.filePath,
47
- apply: `${link.internalLinkName}`
48
+ apply: `${link.name}`
48
49
  };
49
- });
50
+ }).filter(Boolean);
50
51
  if (options.length === 0) return null;
51
52
  return {
52
53
  from,
53
- options,
54
+ options: options || [],
54
55
  validFor: /^[^\]|]*/
55
56
  };
56
57
  }
@@ -25,14 +25,14 @@ export const editorLinkClickPlugin = EditorView.domEventHandlers({
25
25
  const path = anchor.dataset.path;
26
26
  if (!path) return true;
27
27
  const linkMap = view.state.facet(internalLinkMapFacet);
28
- const linkInfo = linkMap.find((l) => l.internalLinkName === path);
28
+ const linkInfo = linkMap.find((l) => l.name === path || l.filePath === path);
29
29
  const type = anchor.dataset.type;
30
30
  const detail = {
31
- path,
31
+ target: path,
32
32
  subpath: anchor.dataset.subpath,
33
33
  display: anchor.dataset.display,
34
34
  type: type || "internal-link",
35
- redirectToPath: linkInfo?.redirectToPath
35
+ referenceId: anchor.dataset.referenceId || linkInfo?.referenceId
36
36
  };
37
37
  view.dom.dispatchEvent(new CustomEvent("internal-link-click", {
38
38
  bubbles: true,
@@ -22,7 +22,7 @@ function buildInternalLinkDecorations(state) {
22
22
  const pathNode = node.node.getChild("InternalLink")?.getChild("InternalPath");
23
23
  if (pathNode) {
24
24
  const path = state.doc.sliceString(pathNode.from, pathNode.to);
25
- const linkInfo = linkMap.find((l) => l.internalLinkName === path || l.filePath === path);
25
+ const linkInfo = linkMap.find((l) => l.name === path || l.filePath === path);
26
26
  if (linkInfo?.embedComponent) {
27
27
  const line = state.doc.lineAt(node.from);
28
28
  const props = { linkData: linkInfo };
@@ -58,7 +58,7 @@ function buildInternalLinkDecorations(state) {
58
58
  const aliasNode = contentContainerNode.getChild("InternalDisplay");
59
59
  const subpath = subpathNode ? state.doc.sliceString(subpathNode.from, subpathNode.to) : void 0;
60
60
  const alias = aliasNode ? state.doc.sliceString(aliasNode.from, aliasNode.to) : void 0;
61
- const linkInfo = linkMap.find((l) => l.internalLinkName === path || l.filePath === path);
61
+ const linkInfo = linkMap.find((l) => l.name === path || l.filePath === path);
62
62
  if (node.name === "Embed" && linkInfo?.embedComponent) {
63
63
  return false;
64
64
  }
@@ -71,6 +71,8 @@ function buildInternalLinkDecorations(state) {
71
71
  };
72
72
  if (!linkInfo) {
73
73
  linkAttributes["class"] += " cm-unresolved-link";
74
+ } else {
75
+ linkAttributes["data-reference-id"] = linkInfo.referenceId;
74
76
  }
75
77
  if (subpath) linkAttributes["data-subpath"] = subpath;
76
78
  if (alias) linkAttributes["data-display"] = alias;
@@ -2,11 +2,34 @@ import type { Component } from 'vue'
2
2
  import type { LanguageSupport } from '@codemirror/language'
3
3
  import type { SyntaxNode } from '@lezer/common'
4
4
 
5
+ /**
6
+ * Represents a resolvable internal link within the editor.
7
+ * This is used to provide autocompletion and to resolve link targets.
8
+ */
5
9
  export interface InternalLink {
6
- internalLinkName: string;
7
- filePath?: string;
8
- redirectToPath: string;
9
- embedComponent?: Component;
10
+ /**
11
+ * The display name of the link.
12
+ * This is what appears in the autocompletion list and is used as the default link text.
13
+ * It should be unique if `filePath` is not provided.
14
+ */
15
+ name: string;
16
+ /**
17
+ * An optional file path associated with the link.
18
+ * Used to disambiguate links that have the same `name`.
19
+ * When autocompleting a link with a duplicate name, this path is used as the link target.
20
+ */
21
+ filePath?: string;
22
+ /**
23
+ * A unique identifier for the internal link.
24
+ * This value has no direct effect on the editor's behavior but is passed through
25
+ * to click events, allowing developers to use it for their own logic (e.g., routing).
26
+ */
27
+ referenceId: string;
28
+ /**
29
+ * An optional Vue component to render when this link is embedded (e.g., `![[link]]`).
30
+ * If not provided, embeds of this link will be rendered as standard links.
31
+ */
32
+ embedComponent?: Component;
10
33
  }
11
34
 
12
35
  export interface InternalLinkNode {
@@ -15,12 +38,29 @@ export interface InternalLinkNode {
15
38
  display?: string
16
39
  }
17
40
 
41
+ /**
42
+ * Details about a clicked internal link.
43
+ * This object is emitted as the payload of the `internal-link-click` event.
44
+ */
18
45
  export interface InternalLinkClickDetail {
19
- path: string;
20
- subpath?: string;
21
- display?: string;
22
- type: 'embed' | 'internal-link';
23
- redirectToPath?: string;
46
+ /**
47
+ * The target of the link as specified in the markdown source.
48
+ * This corresponds to the `path` part in `[[path#subpath|display]]`.
49
+ * It can be either the `name` or `filePath` of an `InternalLink`.
50
+ */
51
+ target: string;
52
+ /** The subpath of the link, if any. Corresponds to the `#subpath` part. */
53
+ subpath?: string;
54
+ /** The display text (alias) of the link, if any. Corresponds to the `|display` part. */
55
+ display?: string;
56
+ /** The type of interaction, either a direct link click or an embed. */
57
+ type: 'embed' | 'internal-link';
58
+ /**
59
+ * The unique identifier (`referenceId`) of the resolved `InternalLink`.
60
+ * This allows developers to identify which link was clicked, even if multiple links
61
+ * share the same name or file path.
62
+ */
63
+ referenceId?: string;
24
64
  }
25
65
 
26
66
  export interface ExternalLinkClickDetail {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@type32/codemirror-rich-obsidian-editor",
3
- "version": "0.0.24",
3
+ "version": "0.0.25",
4
4
  "description": "OFM Editor Component for Nuxt.",
5
5
  "repository": "Type-32/codemirror-rich-obsidian",
6
6
  "license": "MIT",