import type { BindingSourceExpression } from '../binding/source.js'; import { type Registry } from './registry.js'; /** * css module source mapping — host vite plugin (pygmalion-inspect-plugin) * window.__PYG_CSS_MODULES__ Derived from registration stacked in an array (scope class → scss file/local name). */ export interface SourceTarget { /** .module.scss path (relative to host project root). */ file: string; /** The component file that imported the scss (text write-back target). */ componentFile: string; /** Local class name (e.g. row). */ localName: string; } /** Source mapping to the element's first css module class — null if not present (live-only editing). */ export declare function sourceTargetOf(el: Element): SourceTarget | null; /** Target file for text write-back — componentFile of the first css module mapping class of itself/ancestor. */ export declare function nearestComponentFile(el: Element): string | null; /** A safe structured representation of `componentFile|sourceStart|elementName` that the Vite transform injects into the DOM. */ export interface SourceIdentity { componentFile: string; sourceStart: number; elementName: string; } export interface SourceAffectedFrame { id: string; name: string; } export type SourceEditScope = 'instance' | 'shared-source'; export type PrimitiveSourceValue = string | number | boolean; export type PrimitiveSourceValueType = 'string' | 'number' | 'boolean'; export interface SourceInsertComponent { name: string; importPath: string; importKind: 'named' | 'default' | 'namespace'; /** Named export when the local JSX root uses an alias. */ importedName?: string; props: Record; children?: string; } export type SourceEditOperation = { kind: 'prop'; identity: SourceIdentity; prop: string; valueType: 'expression'; /** Exact existing expression bytes, never a sampled DOM value. */ from: string; to: BindingSourceExpression; identifiers: readonly string[]; helpers: readonly string[]; scope: SourceEditScope; affectedFrames: SourceAffectedFrame[]; } | { kind: 'prop'; identity: SourceIdentity; prop: string; from: PrimitiveSourceValue; to: PrimitiveSourceValue; valueType: PrimitiveSourceValueType; scope: SourceEditScope; affectedFrames: SourceAffectedFrame[]; } | { kind: 'text'; identity: SourceIdentity; from: string; to: string; scope: SourceEditScope; affectedFrames: SourceAffectedFrame[]; } | { kind: 'insert'; mode: 'clone'; identity: SourceIdentity; fromIndex: number; toIndex: number; scope: SourceEditScope; affectedFrames: SourceAffectedFrame[]; } | { kind: 'insert'; mode: 'component'; identity: SourceIdentity; slot: 'children'; toIndex: number; component: SourceInsertComponent; scope: SourceEditScope; affectedFrames: SourceAffectedFrame[]; } | { kind: 'remove'; identity: SourceIdentity; fromIndex: number; scope: SourceEditScope; affectedFrames: SourceAffectedFrame[]; } | { kind: 'move'; identity: SourceIdentity; fromIndex: number; toIndex: number; scope: SourceEditScope; affectedFrames: SourceAffectedFrame[]; }; /** Parse strictly to avoid using untrustworthy DOM attributes as file paths/offsets. */ export declare function parseSourceIdentity(raw: string | null | undefined): SourceIdentity | null; export declare function sourceIdentityOf(el: Element): SourceIdentity | null; /** The JSX opening that owns the rendered host element, independent of forwarded component props. */ export declare function ownSourceIdentityOf(el: Element): SourceIdentity | null; /** Primitive literals authored on the owning JSX opening and safe for guarded AST replacement. */ export declare function parsePrimitiveSourceProps(raw: string | null | undefined): Record; /** Number of static direct JSX elements of the slot owner injected by instrumentation. The number of DOM children may differ from the slot, so it is not used. */ export declare function sourceSlotChildCountOf(el: Element): number | null; /** The basic scope of influence for JSX component calls is the corresponding call-site instance, and for intrinsic DOM, the same source element. */ export declare function sourceEditScopeFor(identity: SourceIdentity): SourceEditScope; /** Component props that do not correspond to the DOM, such as variants, are false — the actual results are visible after AST reflection/HMR. */ export declare function canPreviewSourceProp(el: HTMLElement, prop: string): boolean; /** Relative paths whose meaning varies based on the target file are excluded. This slice supports only alias and bare package. */ export declare function isSafeComponentImportPath(importPath: string): boolean; /** Normalize registry metadata only when it can be shared as is with AST payload and UI. */ export declare function sourceInsertComponentFor(registry: Pick, componentName: string): SourceInsertComponent | null; interface ComponentStructurePreview { wrapper: HTMLElement; mount: () => boolean; unmount: () => void; status: () => 'component' | 'placeholder'; } export interface InspectEntry { id: number; kind: 'style' | 'text' | 'prop' | 'structure'; el: HTMLElement; /** Display name — Registered component name or tag.localClass. */ label: string; /** style: CSS property / text: 'text' / prop: JSX prop name. */ prop: string; from: string; to: string; /** Whether the value is a var(--…) token (for log badges). */ token: boolean; /** style write-back target (live only if none). */ source: SourceTarget | null; /** text write-back destination file (live only if none). */ componentFile: string | null; /** If not present, default rule; if present, responsive rule for the corresponding viewport scope. */ media: InspectMedia | null; /** AST-based prop/text reflection target. If not, use the existing CSS/exact text replacement path. */ sourceIdentity: SourceIdentity | null; sourceScope: SourceEditScope | null; valueType: PrimitiveSourceValueType | null; /** Whether the change is previewed immediately. Otherwise it appears after source apply and HMR. */ previewApplied: boolean; affectedFrames: SourceAffectedFrame[]; /** DOM staging metadata for structure entries only. */ structureKind?: 'insert' | 'remove' | 'move'; structureInsertMode?: 'clone' | 'component'; structureParent?: HTMLElement; structureClone?: HTMLElement; structureComponent?: SourceInsertComponent; structurePreview?: ComponentStructurePreview; } export interface InspectMedia { minWidth?: number; maxWidth?: number; } /** Source edit payload posted through a host-provided write-back endpoint. */ export interface InspectApplyPayload { /** Optional exact source snapshot required by canonical document output. */ sourceProof?: { sourceRevision: string; files: readonly { file: string; sha256: string; }[]; }; /** Final bundle of declarations per (scss file, local class). */ styles: { file: string; className: string; /** Exact existing descendant-class selector; omission uses the default class lookup. */ selector?: string; componentFile: string; declarations: Record; affectedFrames: SourceAffectedFrame[]; media?: InspectMedia; }[]; /** Initial text for each element → Final text (assuming one-time exact matching substitution in the source). */ texts: { componentFile: string; oldText: string; newText: string; affectedFrames: SourceAffectedFrame[]; }[]; /** AST-based prop/text editing using injected JSX source locations. */ operations: SourceEditOperation[]; } export declare class InspectSession { /** The registry of the editor this session edits. */ private readonly registry; constructor(registry: Pick); /** Final commit candidate. Only one combination of the same element, attribute, and breakpoint is always maintained. */ entries: InspectEntry[]; toastMsg: string; toastSeq: number; /** Bumps on every change to the entries or the history; panels compare it. */ revision: number; private seq; /** Undo/Redo is a session memory cache separate from the final patch. */ private undoHistory; private redoHistory; /** Original value before editing for each (element, property, breakpoint). */ private originals; private listeners; get canUndo(): boolean; get canRedo(): boolean; subscribe(fn: () => void): () => void; private notify; reset(): void; toast(msg: string): void; private scopeKey; private originalValue; private setOriginalValue; private sameTarget; private pendingFor; /** Fold 1 manipulation into the final patch. When it returns to its original value, it is removed from the commit candidates. */ private updatePending; private pushOperation; /** Commit style edits — apply DOM immediately + record history + merge final patches. */ recordStyle(el: HTMLElement, prop: string, value: string, label: string, media?: InspectMedia | null, affectedFrames?: readonly SourceAffectedFrame[]): void; hasPendingValueEdit(el: HTMLElement): boolean; hasPendingStructuralEdit(el: HTMLElement): boolean; structuralEditKind(el: HTMLElement): InspectEntry['structureKind'] | null; /** A source element stages only one value or structure, and a DOM parent stages only one structure change. */ structuralBlockReason(el: HTMLElement, requested: 'insert' | 'remove' | 'move'): string | null; /** Check whether the registry component can be placed in the children slot of the selection element. */ componentInsertBlockReason(el: HTMLElement, targetComponentName?: string | null): string | null; /** Text edit commit — Only one final text per element is kept as a commit candidate. */ recordText(el: HTMLElement, newText: string, label: string, affectedFrames?: readonly SourceAffectedFrame[]): boolean; /** Editing primitive component props — Possible DOM properties are reflected immediately, variants, etc. are reflected after application/HMR. */ recordProp(el: HTMLElement, prop: string, originalValue: PrimitiveSourceValue, newValue: PrimitiveSourceValue, label: string, identity?: SourceIdentity | null, affectedFrames?: readonly SourceAffectedFrame[]): boolean; recordDuplicate(el: HTMLElement, label: string, affectedFrames?: readonly SourceAffectedFrame[]): boolean; /** Inserts the registry component directly into the children slot of the selected element. */ recordComponentInsert(slotOwner: HTMLElement, componentName: string, toIndex: number, label: string, targetComponentName?: string | null, affectedFrames?: readonly SourceAffectedFrame[]): boolean; recordRemove(el: HTMLElement, label: string, affectedFrames?: readonly SourceAffectedFrame[]): boolean; recordMove(el: HTMLElement, direction: -1 | 1, label: string, affectedFrames?: readonly SourceAffectedFrame[]): boolean; private applyValue; undo(): void; redo(): void; /** Returns all unapplied operations to their original values. Redo cache is preserved. */ restoreOriginal(): void; /** Restore every unapplied change and clear undo and redo history. */ discardPending(): void; /** Build the apply payload using AST operations or the legacy CSS and exact-text path. */ buildApplyPayload(): InspectApplyPayload; /** Impact graph results are reflected in the current source operation. If they are the same, they are not re-rendered. */ updateAffectedFrames(frames: readonly SourceAffectedFrame[]): void; /** After successful reflection — only the mapped and reflected entries are emptied from the log (leaving live-only entries). */ clearApplied(): void; } export declare function isSourceBackedEntry(entry: InspectEntry): boolean; export {};