-
Notifications
You must be signed in to change notification settings - Fork 7
ENG-519 Make human-readable labels for node ids #1271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
0b0a483
db4605f
fd3f363
96c67a1
e05dfa6
3776d1a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| import type { | ||
| AppWithUnofficialApis, | ||
| PropertyWidget, | ||
| PropertyWidgetComponentBase, | ||
| } from "~/utils/obsidianUnofficialTypes"; | ||
| import type DiscourseGraphPlugin from "~/index"; | ||
| import { getNodeTypeById } from "~/utils/typeUtils"; | ||
|
|
||
| export const NODE_TYPE_ID_PROPERTY_KEY = "nodeTypeId"; | ||
| const WIDGET_TYPE = "dg-node-type-id"; | ||
|
|
||
| type NodeTypeIdPropertyWidgetComponent = PropertyWidgetComponentBase; | ||
|
|
||
| type NodeTypeIdPropertyWidget = | ||
| PropertyWidget<NodeTypeIdPropertyWidgetComponent>; | ||
|
|
||
| /** | ||
| * Obsidian's frontmatter Properties UI (reading view + live preview) renders each | ||
| * property via a widget looked up by `metadataTypeManager`. This is unofficial/ | ||
| * internal API (see `obsidian-typings`), so it degrades gracefully: if Obsidian | ||
| * ever drops support, the widget type is simply unrecognized and the property | ||
| * renders as its raw text value, same as before this widget existed. | ||
| */ | ||
| const createWidget = ( | ||
| plugin: DiscourseGraphPlugin, | ||
| ): NodeTypeIdPropertyWidget => ({ | ||
| type: WIDGET_TYPE, | ||
| icon: "shapes", | ||
| name: () => "Discourse node type", | ||
| validate: (value: unknown) => typeof value === "string", | ||
| 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); | ||
| el.tabIndex = 0; | ||
|
|
||
| return { | ||
| type: WIDGET_TYPE, | ||
| focus: () => el.focus(), | ||
| }; | ||
| }, | ||
| }); | ||
|
|
||
| export const registerNodeTypeIdPropertyWidget = ( | ||
| plugin: DiscourseGraphPlugin, | ||
| ): void => { | ||
| const metadataTypeManager = (plugin.app as AppWithUnofficialApis) | ||
| .metadataTypeManager; | ||
|
|
||
| if (metadataTypeManager) | ||
| try { | ||
| metadataTypeManager.registeredTypeWidgets[WIDGET_TYPE] = | ||
| createWidget(plugin); | ||
| void metadataTypeManager.setType(NODE_TYPE_ID_PROPERTY_KEY, WIDGET_TYPE); | ||
| } catch (error) { | ||
| console.error(error); | ||
| } | ||
| }; | ||
|
maparent marked this conversation as resolved.
|
||
|
|
||
| export const unregisterNodeTypeIdPropertyWidget = ( | ||
| plugin: DiscourseGraphPlugin, | ||
| ): void => { | ||
| const metadataTypeManager = (plugin.app as AppWithUnofficialApis) | ||
| .metadataTypeManager; | ||
|
|
||
| if (metadataTypeManager) | ||
| try { | ||
| if ( | ||
| metadataTypeManager.getAssignedWidget(NODE_TYPE_ID_PROPERTY_KEY) === | ||
| WIDGET_TYPE | ||
| ) { | ||
| void metadataTypeManager.unsetType(NODE_TYPE_ID_PROPERTY_KEY); | ||
| } | ||
| delete metadataTypeManager.registeredTypeWidgets[WIDGET_TYPE]; | ||
| } catch (error) { | ||
| console.error(error); | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| import { App, Events, Debouncer, Plugin, Component } from "obsidian"; | ||
|
Check warning on line 1 in apps/obsidian/src/utils/obsidianUnofficialTypes.ts
|
||
|
|
||
| // extracted the MetaadataTypeManager from "obsidian-types", | ||
| // and parts of internalPlugins, plugins. | ||
|
|
||
| type FocusMode = "both" | "end" | "start"; | ||
|
|
||
| export type PropertyWidgetComponentBase = { | ||
| /** | ||
| * The type of the property widget. | ||
| */ | ||
| type: string; | ||
| /** | ||
| * Focus the property widget. | ||
| * | ||
| * @param mode - The focus mode. | ||
| */ | ||
| focus(mode?: FocusMode): void; | ||
| }; | ||
|
|
||
| type PropertyRenderContext = { | ||
| /** | ||
| * Reference to the app. | ||
| */ | ||
| app: App; | ||
| /** | ||
| * Key of the property field. | ||
| */ | ||
| key: string; | ||
| /** | ||
| * Determine the source path of current context. | ||
| */ | ||
| sourcePath: string; | ||
| /** | ||
| * Callback called on property field unfocus. | ||
| */ | ||
| blur(): void; | ||
| /** | ||
| * Callback called on property value change. | ||
| * | ||
| * @param value - The new property value. | ||
| */ | ||
| onChange(value: unknown): void; | ||
| }; | ||
|
|
||
| export type PropertyWidget< | ||
| ComponentType extends | ||
| PropertyWidgetComponentBase = PropertyWidgetComponentBase, | ||
| > = { | ||
| /** | ||
| * Lucide-dev icon associated with the widget. | ||
| */ | ||
| icon: string; | ||
| /** | ||
| * Reserved keys for the widget. | ||
| */ | ||
| reservedKeys?: string[]; | ||
| /** | ||
| * Identifier for the widget. | ||
| */ | ||
| type: string; | ||
| /** | ||
| * Returns the I18N name of the widget. | ||
| * | ||
| * @returns The localized name of the widget. | ||
| */ | ||
| name(): string; | ||
| /** | ||
| * Render function for the widget on field container given context and data. | ||
| * | ||
| * @param containerEl - The container element to render the widget into. | ||
| * @param data - The property data to render. | ||
| * @param context - The rendering context for the property. | ||
| * @returns The rendered widget component. | ||
| */ | ||
| render( | ||
| containerEl: HTMLElement, | ||
| data: unknown, | ||
| context: PropertyRenderContext, | ||
| ): ComponentType; | ||
| /** | ||
| * Validate whether the input value to the widget is correct. | ||
| * | ||
| * @param value - The value to validate. | ||
| * @returns Whether the value is valid. | ||
| */ | ||
| validate(value: unknown): boolean; | ||
| }; | ||
|
|
||
| type PropertyWidgetEntry = { | ||
|
Check warning on line 90 in apps/obsidian/src/utils/obsidianUnofficialTypes.ts
|
||
| /** | ||
| * Display name of the property widget. | ||
| */ | ||
| name: string; | ||
| /** | ||
| * The property widget type. | ||
| */ | ||
| widget: string; | ||
| }; | ||
|
|
||
| type PropertyInfo = { | ||
|
Check warning on line 101 in apps/obsidian/src/utils/obsidianUnofficialTypes.ts
|
||
| /** | ||
| * Name of property. | ||
| */ | ||
| name: string; | ||
| /** | ||
| * Usage count of property. | ||
| */ | ||
| occurrences: number; | ||
| /** | ||
| * Type of property. | ||
| */ | ||
| widget: string; | ||
| }; | ||
|
|
||
| type TypeInfo = { | ||
|
Check warning on line 116 in apps/obsidian/src/utils/obsidianUnofficialTypes.ts
|
||
| /** | ||
| * The explicitly assigned property widget type. | ||
| */ | ||
| expected: PropertyWidget; | ||
| /** | ||
| * The property widget type inferred from the value. | ||
| */ | ||
| inferred: PropertyWidget; | ||
| }; | ||
|
|
||
| type PropertyWidgetType = string; | ||
|
|
||
| type MetadataTypeManager = { | ||
| /** | ||
| * Registered type widgets. | ||
| */ | ||
| registeredTypeWidgets: Record<PropertyWidgetType, PropertyWidget>; | ||
| /** | ||
| * Get assigned widget type for property. | ||
| * | ||
| * @param property - Property name. | ||
| * @returns The assigned widget type, or `null`. | ||
| */ | ||
| getAssignedWidget(property: string): null | PropertyWidgetType; | ||
| /** | ||
| * Set widget type for property. | ||
| * | ||
| * @param property - Property name. | ||
| * @param type - Widget type to assign. | ||
| * @returns A promise that resolves when the widget type is set. | ||
| */ | ||
| setType(property: string, type: PropertyWidgetType): Promise<void>; | ||
| /** | ||
| * Unset widget type for property. | ||
| * | ||
| * @param property - Property name. | ||
| * @returns A promise that resolves when the widget type is unset. | ||
| */ | ||
| unsetType(property: string): Promise<void>; | ||
| } & Events; | ||
|
|
||
| export type InternalPluginInstance = { | ||
| plugin: InternalPlugin; | ||
| }; | ||
|
|
||
| type InternalPlugin = { | ||
| enabled: boolean; | ||
| instance: InternalPluginInstance; | ||
| } & Component; | ||
|
|
||
| export type AppWithUnofficialApis = App & { | ||
| appId: string; | ||
| metadataTypeManager?: MetadataTypeManager; | ||
| plugins?: Events & { plugins?: Record<string, Plugin> }; | ||
| internalPlugins?: Events & { | ||
| plugins?: Record<string, InternalPlugin>; | ||
| }; | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.