Skip to content

ENG-519 Make human-readable labels for node ids - #1271

Draft
maparent wants to merge 2 commits into
mainfrom
eng-519-make-human-readable-labels-for-node-ids
Draft

ENG-519 Make human-readable labels for node ids#1271
maparent wants to merge 2 commits into
mainfrom
eng-519-make-human-readable-labels-for-node-ids

Conversation

@maparent

@maparent maparent commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

@linear-code

linear-code Bot commented Jul 30, 2026

Copy link
Copy Markdown

ENG-519

@supabase

supabase Bot commented Jul 30, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project zytfjzqyijgagqxrzbmz because there are no changes detected in packages/database/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
discourse-graph Ready Ready Preview Jul 30, 2026 10:18pm

Request Review

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

View 2 additional findings in Devin Review.

Open in Devin Review

Comment on lines +26 to +34
render: (containerEl: HTMLElement, data: unknown) => {
const nodeTypeId = typeof data === "string" ? data : String(data ?? "");
const nodeType = getNodeTypeById(plugin, nodeTypeId);

const el = containerEl.createSpan({
cls: "dg-node-type-id-value",
text: nodeType?.name ?? nodeTypeId,
});
el.setAttr("title", nodeTypeId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Node type property shows a meaningless placeholder instead of the type name

The value handed to the property display is converted straight to text (String(data ?? "") at apps/obsidian/src/utils/nodeTypeIdPropertyWidget.ts:27) even though it arrives as a wrapper object rather than plain text, so the property shows [object Object] instead of the readable node type name.
Impact: Every note's node type property in the properties panel can display an unreadable placeholder instead of the human-readable label the feature is meant to add.

Widget render callback receives a property entry object, not the raw value

Obsidian's internal property widget contract (as typed by obsidian-typings) is render(containerEl, data: PropertyEntryData<T>, ctx), where PropertyEntryData is { key, type, value }. The callback here declares data: unknown (which is bivariantly assignable, so the type checker does not catch it) and then treats data as if it were the raw string value. Since the object is not a string, typeof data === "string" is false and String(data) yields "[object Object]"; getNodeTypeById(plugin, "[object Object]") returns undefined, so the fallback text (and the title attribute at apps/obsidian/src/utils/nodeTypeIdPropertyWidget.ts:34) is that placeholder. The fix is to read data.value (guarding for non-string) before looking up the node type.

Prompt for agents
In apps/obsidian/src/utils/nodeTypeIdPropertyWidget.ts the widget's render callback assumes its second argument is the raw frontmatter value. Obsidian's property widget API passes a property entry object shaped like { key, type, value }. Because the parameter is declared as `unknown`, TypeScript does not flag the mismatch, and at runtime `String(data)` produces "[object Object]", so the widget shows that placeholder instead of the node type name (and also as the tooltip). Update the render implementation to extract the value field (defensively handling both a raw string and the entry object shape) before calling getNodeTypeById, and consider verifying against obsidian-typings' PropertyEntryData/PropertyRenderContext types instead of `unknown`.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +62 to +67
if (
metadataTypeManager.getAssignedWidget(NODE_TYPE_ID_PROPERTY_KEY) ===
WIDGET_TYPE
) {
void metadataTypeManager.unsetType(NODE_TYPE_ID_PROPERTY_KEY);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Property type assignment is left behind in the vault when the plugin is disabled

The check that decides whether to remove the custom display setting compares a widget object against a plain name (getAssignedWidget(...) === WIDGET_TYPE at apps/obsidian/src/utils/nodeTypeIdPropertyWidget.ts:63-65), which never matches, so the setting is never removed and stays in the vault after the plugin is turned off.
Impact: After disabling or uninstalling the plugin, the node type property keeps pointing at a display style that no longer exists, so it may render oddly until the user fixes it manually.

getAssignedWidget returns the widget object, not the type string

Obsidian's MetadataTypeManager.getAssignedWidget(key) resolves the assigned type name and returns the corresponding entry from registeredTypeWidgets (i.e. a PropertyWidget object); the string type name is available via getAssignedType(key). Comparing the returned object to the string "dg-node-type-id" is always false, so unsetType is never invoked while delete metadataTypeManager.registeredTypeWidgets[WIDGET_TYPE] at apps/obsidian/src/utils/nodeTypeIdPropertyWidget.ts:68 still removes the widget, leaving the persisted assignment (in the vault's types.json, written by setType at apps/obsidian/src/utils/nodeTypeIdPropertyWidget.ts:52) dangling.

Suggested change
if (
metadataTypeManager.getAssignedWidget(NODE_TYPE_ID_PROPERTY_KEY) ===
WIDGET_TYPE
) {
void metadataTypeManager.unsetType(NODE_TYPE_ID_PROPERTY_KEY);
}
if (metadataTypeManager.getAssignedType(NODE_TYPE_ID_PROPERTY_KEY) === WIDGET_TYPE) {
void metadataTypeManager.unsetType(NODE_TYPE_ID_PROPERTY_KEY);
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +44 to +53
export const registerNodeTypeIdPropertyWidget = (
plugin: DiscourseGraphPlugin,
): void => {
const metadataTypeManager = (
plugin.app as unknown as { metadataTypeManager: MetadataTypeManager }
).metadataTypeManager;

metadataTypeManager.registeredTypeWidgets[WIDGET_TYPE] = createWidget(plugin);
void metadataTypeManager.setType(NODE_TYPE_ID_PROPERTY_KEY, WIDGET_TYPE);
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Plugin can fail to start entirely if the internal properties API is unavailable

The internal properties registry is used without first checking that it exists (metadataTypeManager.registeredTypeWidgets[WIDGET_TYPE] = ... at apps/obsidian/src/utils/nodeTypeIdPropertyWidget.ts:51), so if that unofficial capability is missing the startup step throws and the rest of the plugin never initializes.
Impact: On an Obsidian version without this internal capability, the whole plugin would fail to load instead of just losing the nicer property label.

Unguarded internal API access during onload

registerNodeTypeIdPropertyWidget(this) is called in onload at apps/obsidian/src/index.ts:75 without a try/catch, unlike neighbouring risky initializations (FileChangeListener at apps/obsidian/src/index.ts:86-92, TagNodeHandler at apps/obsidian/src/index.ts:167-173). The cast plugin.app as unknown as { metadataTypeManager: MetadataTypeManager } asserts existence; if app.metadataTypeManager is undefined (or setType/registeredTypeWidgets change shape), a TypeError propagates out of onload, skipping registerCommands, settings tab, and view registrations. This contradicts the module comment at apps/obsidian/src/utils/nodeTypeIdPropertyWidget.ts:12-18 claiming graceful degradation. Same unguarded access exists in unregisterNodeTypeIdPropertyWidget.

Prompt for agents
apps/obsidian/src/utils/nodeTypeIdPropertyWidget.ts accesses the unofficial app.metadataTypeManager API via a type assertion and immediately mutates registeredTypeWidgets and calls setType. The module comment promises graceful degradation if Obsidian drops support, but there is no runtime guard, and registerNodeTypeIdPropertyWidget is called unguarded from onload (apps/obsidian/src/index.ts:75), so a missing/renamed API would throw and abort plugin initialization. Add defensive checks (verify the manager and the methods/properties exist, or wrap in try/catch with a console warning) in both register and unregister helpers so the feature silently no-ops.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@maparent
maparent marked this pull request as draft July 30, 2026 21:02
@maparent
maparent force-pushed the eng-519-make-human-readable-labels-for-node-ids branch from b761a64 to 4956cbc Compare July 30, 2026 22:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant