Skip to content

feat: links, file targets and editable fields in the property panel - #100

Merged
endrix merged 7 commits into
mainfrom
feat/panel-links
Oct 24, 2026
Merged

endrix merged 7 commits into
mainfrom
feat/panel-links

Conversation

@endrix

@endrix endrix commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

Summary

The property panel shows a node's text, but nothing in it led anywhere: a webview doesn't navigate, so a link in a rendered body or a producer's fact did nothing, and a file the text named had to be found by hand. This lets a producer make its panel the place a reader works from:

  • Links lead somewhere. One click listener sits on the panel.
    • #<id> selects that element and centres it. The id can be the diagram's own or the producer's, now kept on every node as wf:graphNodeId, since the diagram names a node after its label.
    • When the diagram doesn't draw the element, a file reference in the link's title ([X](#id "/abs/doc.md#L12")) opens instead.
    • http(s): and mailto: go to the browser.
    • Anything else goes to the host as a reference to resolve (open-reference.ts), tried in this order:
      1. an absolute path or file: URI;
      2. a path relative to the node's own file, then each folder above it up to its workspace folder;
      3. the workspace's files ending with that path, closest first, with a quick pick when several match.
    • #L12, #L12-L20 and :12-20 select lines; ::Name.member and #name land on the symbol's definition, else its first mention.
  • meta.open: { file, line? } (wf:open): the file a node stands for, such as the code a specification clause names.
    • A double-click opens it at its line, ahead of a drill-down, the node's views or its definition.
    • The viewer listener stands back, so the file opens once rather than beside the definition.
    • Basic Info shows it as an Opens link.
  • Producer-editable fields in any profile. A meta.parameters entry marked editable: true gets a Fields section, committed through the existing updateNodeParameter as typed (unquoted).
    • choices gives a select; suggestions gives an input with a list.
    • Until now the panel edited parameters only for a network runtime (networkPropertySections), as source expressions with an add row. That editor is unchanged.
  • clientBehavior.propertySections: { ports?, annotations?, parameters?, entityType? }: false hides the port lists, the raw definition annotations, the definition-parameters section, or Basic Info's task/workflow Type row and its change-type button.

All neutral: the platform reads link syntax, a file target and field metadata; the product says what they are.

A double-click opens one thing (a later commit): on a node that isn't a network instance, the network navigation listener opens its first view (or, with doubleClickOpensSource, its definition), and the viewer listener also opened its definition as a preview. A specification clause opened its refinement trace and its heading at once. The viewer listener now steps aside whenever the other one opens something; a node nothing else opens still gets its definition (double-click-opens-one-thing.test.ts, 3 tests).

Merging with #92 (Facts): the two conflict where each adds a section under Basic Info. I'd resolve it as Basic Info, then Fields, then Facts (the integration branch does), so the values a person edits sit above facts that end with a long body.

Test plan

  • npm run typecheck, npm run check:neutrality, all four suites
  • New tests:
    • open-target.test.ts (7): the open target from a node or its ancestors; a real, non-preview open at the 0-based line; link classification (element, element with a file fallback in its title, URL, reference, script refused); the reference action's flags and base.
    • producer-fields.test.ts (3).
    • node-open-target.test.ts (4): the server maps meta.open, keeps wf:graphNodeId, and passes editable parameters through.
    • open-reference.test.ts (9): parsing every form, the folder walk, trailing-path search ranking, symbol lines.
  • Headless, on the integration branch with the real client and a picorv32 NDF document:
    • The panel shows Basic Info, Fields (5) and Facts.
    • A link to a clause the diagram draws selects it; one it doesn't draw sends the open-reference action for its file at the heading line.
    • picorv32.v:1202-1210 sends the open-reference action with the clause's document as base.
    • A double-click on a source clause sends exactly one navigate action, to gen/membus/memif.py at the def hold_take line.
  • Live in VS Code: the host resolving and opening those references (the quick pick for picorv32.v, which several files match), and a field edit round trip through ndf-sidecar.

https://claude.ai/code/session_01E8WfqKknDNN6xpNVcJL5K8

The panel shows a node's text, but nothing in it led anywhere: a webview
does not navigate, so a link in a rendered body or a producer's fact did
nothing, and a file the text named had to be found by hand.

- Links: one click listener on the panel. `#<id>` selects that element and
  centres it; `http(s):`/`mailto:` go to the browser; anything else goes to
  the host flagged `wf:openReference`, which resolves it (open-reference.ts):
  an absolute path or `file:` URI; relative to the node's own file and the
  folders above it up to its workspace folder; else the workspace's files
  ending with that path, closest first, a quick pick when several match.
  `#L12`, `#L12-L20`, `:12-20` select lines; `::Name.member` and `#name`
  land on the symbol's definition, else its first mention.
- `meta.open: { file, line? }` (`wf:open`): the file a node stands for. A
  double-click opens it at its line, ahead of a drill-down, views or the
  definition (the viewer listener stands back so it opens once), and Basic
  Info links it.
- Producer-editable fields: `meta.parameters` entries marked
  `editable: true` get a Fields section in any profile, committed through
  `updateNodeParameter` as typed, a select for `choices`, an input with a
  list for `suggestions`. The network runtime's parameter editor is
  unchanged.
- `clientBehavior.propertySections: { ports?, annotations? }` hides the port
  lists or the raw definition annotations.

Claude-Session: https://claude.ai/code/session_01E8WfqKknDNN6xpNVcJL5K8
The diagram names a node after its label, so a producer's `#<node id>`
link named nothing. Every node now carries its graph-document id as
`wf:graphNodeId`, and the panel looks a link up by either id.

Claude-Session: https://claude.ai/code/session_01E8WfqKknDNN6xpNVcJL5K8
…t drawn

A producer links a node it may not draw in this view (a clause of another
document, a trace's far end). The link's title, when it is a file
reference, is where the panel goes instead of saying the element is
missing.

Claude-Session: https://claude.ai/code/session_01E8WfqKknDNN6xpNVcJL5K8
…tion

A product whose nodes have no parameters of their own (only the fields it
marks editable) saw an empty "Definition Parameters (0)" section on every
node it does not let a person edit.

Claude-Session: https://claude.ai/code/session_01E8WfqKknDNN6xpNVcJL5K8
The Type row names the platform's kind of node (task, workflow) and offers
to change it, which is wrong information for a product whose node kinds
are its own: a specification clause drawn from another document read as
"Workflow", with a button that changes nothing it has.

Claude-Session: https://claude.ai/code/session_01E8WfqKknDNN6xpNVcJL5K8
Two listeners answer a double-click on a node that is not a network
instance: the network navigation listener opens its first view, or its
definition with doubleClickOpensSource, and the viewer listener opened the
definition as a preview too. A specification clause opened its refinement
trace and its heading at once. The viewer listener stands back whenever the
other opens something; a node nothing else opens still gets its definition.

Claude-Session: https://claude.ai/code/session_01E8WfqKknDNN6xpNVcJL5K8
…afe decode

Review of #100:

- The viewer listener stood back for any node with a referenced file and
  views, but the navigation listener's `openLeaf` also needs a referenced
  name to open a view by: a node with views and no name opened nothing.
  `leafOpensItself` now asks `openLeaf` itself, so the two never disagree.
- The panel decoded the `wf:open` URI with `decodeURIComponent`, which
  throws on a malformed `%` sequence and took Basic Info down with it; it
  uses the safe decoder `open-target.ts` already had.
- The reference examples in `open-reference.ts` no longer name a product's
  files.

Claude-Session: https://claude.ai/code/session_01E8WfqKknDNN6xpNVcJL5K8
@endrix
endrix merged commit 943bbf0 into main Oct 24, 2026
1 check passed
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