Desde

Concepts

Design-system grounding

How Desde learns your component library's real props and variants, and why edits stay in its vocabulary.

A tool that can edit source is not very interesting on its own. What makes an edit useful is whether it lands in your design system's vocabulary (a KButton with appearance="danger") or as a one-off override that quietly forks your system: <button class="bg-red-600 px-3">.

The first outcome requires the tool to know your components: what props they take, which values those props accept, and where each prop shows up on screen. Desde reads all of that out of the library you already installed.

Manifests, extracted from the library itself#

A manifest is the tool's structured description of one component: its props, their types, which are a fixed set of choices, and where each one renders.

Writing them by hand does not survive contact with a real design system. A mid-sized library is a few hundred components, and every version bump makes your copy a little more wrong.

So Desde does not ask you to. For an installed Vue library it builds a TypeScript program over the library's shipped .vue.d.ts declaration files and walks each component's resolved props type through the TypeScript type checker. That is the same component the compiler uses to answer "what type is this."

Using the real checker rather than reading declaration text is what makes this work. The checker resolves an imported type alias declared in a sibling file (appearance?: ButtonAppearance), generics, and utility types like Pick and Omit. Without that, a prop typed through any of those collapses into an uneditable "unknown" in the inspector, exactly the prop you wanted, because variant props are usually the ones typed as a shared union.

The important property: this needs no source code. Your design system arrives from npm as compiled output plus declarations, and that is enough. A union type in the declarations becomes a dropdown of real values in the inspector, and the agent knows the same list.

Note Full-fidelity extraction from .vue.d.ts covers Vue libraries; React libraries are read through a separate declaration source. Scope in both cases is props. Slots and events are not extracted.

Auto-scan and the cache#

You do not list your libraries anywhere. The first time the Editor needs a manifest (your first inspector click, or the agent's first catalog lookup), it walks your prototype's node_modules (top-level and scoped @org/pkg entries) and probes a short list of conventional declaration roots (dist/types/components, then dist/types, then dist). Any package shipping at least one *.vue.d.ts under one of those becomes a manifest source. The scan is read-only and follows symlinks, so pnpm and workspace layouts work.

Running a TypeScript program over a large library is not cheap. One icon package in the prototype Desde is developed against ships over five hundred components and takes about a second alone. So results are cached on disk under the prototype root:

.desde/manifests/<package>@<version>[@<context>]@v<extractorVersion>.json

The version in the filename is the installed package's own version, read from its package.json. Invalidation is that filename and nothing else: upgrade a library, the name you look for changes, the lookup misses, extraction re-runs. The other two segments work the same way: an optional fingerprint of the active tsconfig, and a version stamp for the extractor itself, so changing either also changes the name you look for. No staleness check to get wrong.

Measured on an eight-package prototype, a cold run costs roughly three seconds; with the cache warm it is a fraction of a second. Expect the first run after npm install to be the slow one. The cache is never a correctness dependency: a corrupt file is ignored and extraction re-runs.

Note .desde/ is added to your repository's local ignore list on boot, so none of this shows up in your commits.

Rendering hints: from clicked text back to the prop#

Extraction tells you a component has a label prop. It does not tell you the words you clicked are that prop.

Click text inside a library component and the DOM you land on belongs to the library: a <label> inside KLabel, inside KInput, several levels below anything you wrote. Matching rendered text against nearby prop values, the obvious approach, both under-attributes (library-rendered DOM offers no editable surface at all) and over-attributes (an ancestor's props bleed onto a child selection).

A rendering hint states the mapping forward instead. Two kinds: a dom hint ("this component's default slot renders as the text content of its root element") and a forward hint ("this component's label prop is passed into the default slot of the KLabel it renders").

With those, routing an edit is a walk rather than a guess. The tool identifies which prop or slot of the innermost component the clicked element corresponds to, then walks up the component chain following forward hints until nothing further claims it, and resolves at that component's call site, the tag you actually wrote. Clicking a form label three components deep in the library resolves to label="Email" in your own file.

Hints carry provenance (hand-authored, inferred, or generated) plus a verified flag. Only hand-authored or probe-verified hints may route an edit deterministically. An unverified hint is filtered out entirely, so the tool behaves exactly as if it had none: same refusal, same fallback to the model lane described in Deterministic editing. A wrong hint would produce a confidently wrong edit, which is worse than falling back.

Composition#

Several sources contribute manifests (Storybook, first-party components in your own repo, auto-scanned libraries), consulted in a fixed priority order. For props, the first source with an answer wins. Hand-authored hint sets are supported as a source too, though none ship today; every hint in the tree is inferred or generated.

Rendering hints are composed separately, and that separation is load-bearing. The source with the best props is often not the source with the hints: the TypeScript extractor resolves a library's full prop schema but has no idea where a prop renders, while a hand-authored set knows exactly that and nothing else. So the props winner keeps its props, and the first non-empty set of hints is overlaid on top, otherwise the props winner would shadow the hints and attribution would drop back to guessing.

Why this is the point#

Grounding changes what an edit is. Without it, "make this button destructive" is a styling problem, and every answer to a styling problem is a class or an inline style. With it, the tool knows KButton has an appearance prop, knows its accepted values because they came out of the library's own union type, and knows which one you clicked. The edit becomes appearance="danger", a one-line diff in your design system's vocabulary, correct in dark mode and at every breakpoint because the component already handles that.

The same knowledge feeds the chat agent, so a request phrased in prose lands the same way.