@codecanvas-ide/engine 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ import { type PlacedSibling, type PlacementSpot } from './layoutRules.js';
1
2
  import type { Block, Point } from './types.js';
2
3
  /** Blocks laid out together: a block, or blocks side by side (row) or one above the other (column). */
3
4
  export type Arrangement = {
@@ -16,4 +17,9 @@ interface Size {
16
17
  * Positions are relative to the group's origin; sizes come from the caller.
17
18
  */
18
19
  export declare function arrangeSiblings(blocks: Block[], sizeOf: (id: string) => Size, text: string, gap: number, isFile: boolean): Map<string, Point>;
20
+ /**
21
+ * Where blocks that appeared among arranged siblings should go, by the placement rules (layoutRules.ts).
22
+ * Reads the siblings' code once, for all the new blocks.
23
+ */
24
+ export declare function newBlockSpots(siblings: Block[], text: string): (block: Block, placed: PlacedSibling[]) => PlacementSpot | undefined;
19
25
  export {};
@@ -109,8 +109,7 @@ export declare class CodeCanvas {
109
109
  private diffCache;
110
110
  /** Merge conflicts in the text. The canvas then shows its current side, marking the blocks they touch. */
111
111
  private conflicts;
112
- private incomingBlockEls;
113
- private removedBlockEls;
112
+ private ghostBlockEls;
114
113
  /** Errors from the host (`setDiagnostics`), shown instead of the project's type errors. */
115
114
  private hostDiagnostics;
116
115
  private renaming;
@@ -194,6 +193,8 @@ export declare class CodeCanvas {
194
193
  private changedLinesPreview;
195
194
  /** Removed blocks stay visible, in red, where they were in the base version. They can't be selected. */
196
195
  private renderRemovedBlocks;
196
+ /** Blocks of another version (removed, or incoming in a conflict): drawn, but not part of this file. */
197
+ private drawGhostBlocks;
197
198
  /** Diagnostics grouped by the block that shows them: the innermost visible block containing each error. */
198
199
  private diagnosticsByShownBlock;
199
200
  private updateErrorMarkers;
@@ -247,6 +248,7 @@ export declare class CodeCanvas {
247
248
  restoreCamera?: boolean;
248
249
  }): void;
249
250
  private renderBar;
251
+ private languageLabelParts;
250
252
  private toggleRefs;
251
253
  private showErrorsOf;
252
254
  private diagnosticsInside;
@@ -1,7 +1,7 @@
1
1
  export * from './types.js';
2
2
  export { parseFile, detectIndentUnit, blockAt } from './parse.js';
3
3
  export { Project } from './project.js';
4
- export type { CanvasProject, ConfigFile, FileResolution, ProjectOptions, RenameResult } from './project.js';
4
+ export type { CanvasProject, ConfigFile, FileResolution, ProjectOptions, RenameResult, Workspace } from './project.js';
5
5
  export { RemoteProject } from './remoteProject.js';
6
6
  export type { ProjectServerCall } from './remoteProject.js';
7
7
  export { ProjectServer, callProjectServer } from './projectServer.js';
@@ -53,6 +53,13 @@ export declare function emptyLayout(): FileLayout;
53
53
  export declare function computeGeometry(model: FileModel, layout: FileLayout, hints?: Record<string, Point>, opts?: {
54
54
  resolveOverlaps?: boolean;
55
55
  }): FileGeometry;
56
+ /**
57
+ * Where to draw blocks the open version doesn't have (removed since the diff base, or only in the
58
+ * incoming side of a conflict): where they were in their own version, relative to their parent,
59
+ * or the closest free spot to it if a block of this version is there now.
60
+ * Each block's parent must be in `current`. Blocks inside a collapsed or hidden parent are left out.
61
+ */
62
+ export declare function placeGhostBlocks(ghosts: Block[], ghostGeometry: FileGeometry, currentModel: FileModel, current: FileGeometry): Map<string, Point>;
56
63
  interface Rect {
57
64
  x: number;
58
65
  y: number;
@@ -9,6 +9,7 @@ import type { Block } from './types.js';
9
9
  * - relatedness: which blocks end up next to each other
10
10
  * Plus one shape the arranger always applies, the newspaper: a function on the left, the functions it
11
11
  * calls in a column to its right, in the order it calls them.
12
+ * Placement rules then decide where a block goes that appears later, without moving the others.
12
13
  */
13
14
  export type Zone = 'left' | 'data' | 'types' | 'main' | 'bottom';
14
15
  /** The bands from top to bottom. The left column holds `left` blocks next to all of them. */
@@ -28,7 +29,7 @@ export interface StackRule {
28
29
  export declare const STACK_RULES: StackRule[];
29
30
  /** What a block knows about its siblings, read from its own code. */
30
31
  export interface SiblingFacts {
31
- /** Sibling functions it calls, in the order of their first call. */
32
+ /** Sibling functions it calls and components it renders, in the order of their first use. */
32
33
  calls: string[];
33
34
  /** Every sibling it names (calls included). */
34
35
  uses: Set<string>;
@@ -39,6 +40,30 @@ export interface RelatednessRule {
39
40
  score(a: Block, b: Block, factsOf: (block: Block) => SiblingFacts): number;
40
41
  }
41
42
  export declare const RELATEDNESS_RULES: RelatednessRule[];
43
+ /** A sibling that already has a position, as placement rules see it. */
44
+ export interface PlacedSibling {
45
+ block: Block;
46
+ x: number;
47
+ y: number;
48
+ w: number;
49
+ h: number;
50
+ }
51
+ /** Where a new block should go: on one side of a placed sibling. The closest free spot to it wins. */
52
+ export interface PlacementSpot {
53
+ sibling: PlacedSibling;
54
+ side: 'right' | 'below' | 'above';
55
+ }
56
+ export interface PlacementRule {
57
+ description: string;
58
+ spot(block: Block, placed: PlacedSibling[], factsOf: (block: Block) => SiblingFacts): PlacementSpot | undefined;
59
+ }
60
+ /**
61
+ * Where a block that appears in an arranged file goes (typed in the text, or from another branch).
62
+ * Nothing that is already placed moves. First match wins; with no match it goes next to the block
63
+ * before it in the file.
64
+ */
65
+ export declare const PLACEMENT_RULES: PlacementRule[];
66
+ export declare const zoneOf: (block: Block) => Zone;
42
67
  /**
43
68
  * How many levels of callees the newspaper shows to the right of a function. Deeper calls start a
44
69
  * shape of their own, so a long chain of calls doesn't become one very wide staircase.
@@ -17,10 +17,13 @@ export declare function toCodeDiagnostic(model: FileModel, sf: ts.SourceFile, d:
17
17
  export declare function builderOf(model: FileModel): Builder | undefined;
18
18
  /** Slower part: resolve every name to its declaration and link the blocks. */
19
19
  export declare function computeUsages(model: FileModel): Usage[];
20
+ /** JavaScript files parse as JavaScript: in a `.js` file `<T>(x)` is a comparison, not a type argument. */
21
+ export declare function scriptKindOf(path: string): ts.ScriptKind;
20
22
  export declare function detectIndentUnit(text: string): string;
21
23
  declare class Builder {
22
24
  private sf;
23
25
  private text;
26
+ private isReact;
24
27
  roots: Block[];
25
28
  byId: Map<string, Block>;
26
29
  /**
@@ -32,13 +35,17 @@ declare class Builder {
32
35
  private rangeToBlock;
33
36
  /** Every name a usage could resolve to. Other identifiers skip the (slow) checker. */
34
37
  readonly names: Set<string>;
35
- constructor(sf: ts.SourceFile, text: string);
38
+ /** Setters of React state (`setCount`), keyed like `declToBlock`: using one writes the state. */
39
+ private stateSetters;
40
+ constructor(sf: ts.SourceFile, text: string, isReact: boolean);
36
41
  collectStatements(stmts: readonly ts.Statement[], parent: Block | null, depth: number, declarationsOnly?: boolean): void;
37
42
  private addImport;
38
43
  private addFunctionLike;
39
44
  private addClass;
40
45
  private collectMembers;
41
46
  private addVariableStatement;
47
+ /** `const [count, setCount] = useState(0)`: a state block named `count`; using `setCount` writes it. */
48
+ private addState;
42
49
  private addVariable;
43
50
  private add;
44
51
  /** Include doc comments directly above the node (no blank line in between). */
@@ -1,6 +1,8 @@
1
1
  import ts from 'typescript';
2
2
  import type { TextEdit } from './edits.js';
3
3
  import type { CodeDiagnostic, FileModel, IncomingUsage, Usage } from './types.js';
4
+ import { type ConfigFile, type Workspace } from './workspace.js';
5
+ export type { ConfigFile, Workspace } from './workspace.js';
4
6
  export interface ProjectOptions {
5
7
  /**
6
8
  * Standard library declaration files by file name (`lib.es2022.d.ts` → text), including every
@@ -12,17 +14,6 @@ export interface ProjectOptions {
12
14
  lib?: string[];
13
15
  compilerOptions?: ts.CompilerOptions;
14
16
  }
15
- /**
16
- * The folder's TypeScript settings (`tsconfig.json` or `jsconfig.json`). Path aliases, `baseUrl` and
17
- * module resolution decide which file an import points at.
18
- */
19
- export interface ConfigFile {
20
- /** Project path, e.g. `tsconfig.json`. */
21
- path: string;
22
- text: string;
23
- /** Configs it `extends`, by project path. Only relative `extends` are followed. */
24
- extendedFiles?: Record<string, string>;
25
- }
26
17
  export type RenameResult = {
27
18
  ok: true;
28
19
  edits: Record<string, TextEdit[]>;
@@ -53,8 +44,8 @@ export interface CanvasProject {
53
44
  renameBlock(path: string, blockId: string, newName: string): RenameResult | Promise<RenameResult>;
54
45
  }
55
46
  /**
56
- * Every file of a folder, parsed into blocks, with one shared TypeScript program for resolving
57
- * usages, type errors and renames across files.
47
+ * Every file of a folder, parsed into blocks, with TypeScript programs for resolving usages, type
48
+ * errors and renames across files: one per config file (a monorepo has one per package), else one.
58
49
  *
59
50
  * Two speeds: `setFile` is the per-keystroke path (incremental parse of that file only); usages,
60
51
  * diagnostics and renames go through the language service and are meant to run when typing pauses.
@@ -65,11 +56,14 @@ export declare class Project implements CanvasProject {
65
56
  private projectVersion;
66
57
  private libFiles;
67
58
  private hasLib;
68
- private options;
69
- private service;
59
+ private libOptions;
70
60
  private projectNamesCache;
71
- /** Options from the folder's tsconfig.json, between the defaults and `ProjectOptions.compilerOptions`. */
72
- private configFileOptions;
61
+ private workspace;
62
+ private configPrograms;
63
+ /** Language services by config path, created when a file of that config is first resolved. */
64
+ private services;
65
+ /** Shared by the services, so a file several programs include is parsed once. */
66
+ private documentRegistry;
73
67
  /** Changes when files are added or removed, or the settings change: where imports point may change then. */
74
68
  private fileSetVersion;
75
69
  constructor(files?: Record<string, string>, projectOptions?: ProjectOptions);
@@ -77,8 +71,19 @@ export declare class Project implements CanvasProject {
77
71
  setLibFiles(libFiles: Record<string, string>): void;
78
72
  /** Use the folder's tsconfig.json (or none, with null). */
79
73
  setConfigFile(config: ConfigFile | null): void;
74
+ /** Use the folder's config files and workspace packages (or none, with null). */
75
+ setWorkspace(workspace: Workspace | null): void;
80
76
  private reconfigure;
81
77
  private configure;
78
+ /** Options for a program: the defaults, then the config's, then `ProjectOptions.compilerOptions`. */
79
+ private compilerOptions;
80
+ /** Which config each file uses, and the options of each. Worked out again when files are added or removed. */
81
+ private programs;
82
+ private configOf;
83
+ /** The options a file's imports resolve with: its own config's, whichever program includes it. */
84
+ private optionsOfFile;
85
+ private serviceOf;
86
+ private serviceOfConfig;
82
87
  /** Add or update a file. Parses only this file, incrementally from its previous version. */
83
88
  setFile(path: string, text: string): FileModel;
84
89
  removeFile(path: string): void;
@@ -100,19 +105,19 @@ export declare class Project implements CanvasProject {
100
105
  * Usages in other files are renamed too, including imports (no `as` aliases are added).
101
106
  */
102
107
  renameBlock(path: string, blockId: string, newName: string): RenameResult;
103
- /**
104
- * TypeScript keeps local names stable when renaming an export (`import { newName as old }`).
105
- * Here a rename should update every usage, so import/export specifiers get the plain new name.
106
- */
107
- private withoutImportAlias;
108
+ /** The configs of a file and of the files that can use its blocks: its importers, also through re-exports. */
109
+ private configsUsing;
110
+ /** Files that import `path`, or import a file that re-exports from it (an `index.ts` of a folder or package). */
111
+ private importersThroughReExports;
108
112
  private otherFileResolver;
109
113
  private projectNames;
110
114
  /** Files whose imports resolve to `path`. */
111
115
  private importersOf;
112
- private importedPaths;
116
+ private importsOf;
113
117
  private moduleResolutionHost;
114
118
  private fileExists;
115
119
  private readFile;
116
120
  private directoryExists;
121
+ /** The host of one config's program. Its files are those that use the config; their imports come in from the rest. */
117
122
  private createHost;
118
123
  }
@@ -1,12 +1,13 @@
1
- import { type ConfigFile, type FileResolution, type RenameResult } from './project.js';
1
+ import { type ConfigFile, type FileResolution, type RenameResult, type Workspace } from './project.js';
2
2
  /** Methods a `RemoteProject` can call. Arguments and results are plain data, so any message channel carries them. */
3
- declare const SERVER_METHODS: readonly ["setLibFiles", "setConfigFile", "setFiles", "setFile", "removeFile", "resolve", "renameBlock"];
3
+ declare const SERVER_METHODS: readonly ["setLibFiles", "setConfigFile", "setWorkspace", "setFiles", "setFile", "removeFile", "resolve", "renameBlock"];
4
4
  export type ProjectServerMethod = (typeof SERVER_METHODS)[number];
5
5
  /** A `Project` behind plain-data methods: the worker side of a `RemoteProject`. */
6
6
  export declare class ProjectServer {
7
7
  private project;
8
8
  setLibFiles(libFiles: Record<string, string>): void;
9
9
  setConfigFile(config: ConfigFile | null): void;
10
+ setWorkspace(workspace: Workspace | null): void;
10
11
  setFiles(files: Record<string, string>): void;
11
12
  setFile(path: string, text: string): void;
12
13
  removeFile(path: string): void;
@@ -0,0 +1,30 @@
1
+ import ts from 'typescript';
2
+ import type { FileLanguage } from './types.js';
3
+ /**
4
+ * JavaScript or TypeScript, and whether React mode is on: a `.jsx`/`.tsx` file, a file with JSX in
5
+ * it, or a file that imports React.
6
+ */
7
+ export declare function fileLanguageOf(path: string, sf: ts.SourceFile): FileLanguage;
8
+ export declare function isJsx(node: ts.Node): boolean;
9
+ type FunctionNode = ts.FunctionDeclaration | ts.FunctionExpression | ts.ArrowFunction;
10
+ /** React only treats capitalized names as components: `<cart />` is an HTML tag. */
11
+ export declare const isComponentName: (name: string) => boolean;
12
+ /** A function that returns JSX from its own body (not from a callback inside it). */
13
+ export declare function returnsJsx(fn: ts.FunctionLikeDeclaration): boolean;
14
+ /**
15
+ * The function of a component assigned to a variable: an arrow or function that returns JSX, or
16
+ * the function inside `memo(…)` / `forwardRef(…)`, also nested.
17
+ */
18
+ export declare function componentFunctionOf(initializer: ts.Expression): FunctionNode | undefined;
19
+ /** `class Cart extends Component`, `extends React.PureComponent<Props>` */
20
+ export declare function isComponentClass(cls: ts.ClassLikeDeclaration): boolean;
21
+ /** `const [count, setCount] = useState(0)`: the hook call and the declarations of the value and its setter. */
22
+ export interface StateHook {
23
+ call: ts.CallExpression;
24
+ value?: ts.BindingElement | ts.VariableDeclaration;
25
+ setter?: ts.BindingElement;
26
+ }
27
+ export declare function stateHookOf(declaration: ts.VariableDeclaration): StateHook | undefined;
28
+ /** `useCart`, `useLocalStorage`: React runs functions named like this as hooks. */
29
+ export declare const isHookName: (name: string) => boolean;
30
+ export {};
@@ -1,4 +1,4 @@
1
- import type { CanvasProject, ConfigFile, FileResolution, RenameResult } from './project.js';
1
+ import type { CanvasProject, ConfigFile, FileResolution, RenameResult, Workspace } from './project.js';
2
2
  import type { ProjectServerMethod } from './projectServer.js';
3
3
  import type { FileModel } from './types.js';
4
4
  /** Sends one call to the `ProjectServer` (in a worker) and resolves with its result. Calls arrive in order. */
@@ -21,6 +21,8 @@ export declare class RemoteProject implements CanvasProject {
21
21
  setLibFiles(libFiles: Record<string, string>): void;
22
22
  /** The folder's tsconfig.json (or none, with null). */
23
23
  setConfigFile(config: ConfigFile | null): void;
24
+ /** The folder's config files (one per package in a monorepo) and package.json files (or none, with null). */
25
+ setWorkspace(workspace: Workspace | null): void;
24
26
  getModel(path: string): FileModel | undefined;
25
27
  getText(path: string): string | undefined;
26
28
  paths(): string[];
@@ -1,3 +1,3 @@
1
1
  /** Canvas styles. Injected once. Hosts can override the CSS variables on `.cc-root`. */
2
- export declare const CSS = "\n.cc-root {\n --cc-bg: #15171c;\n --cc-dot: rgba(255,255,255,.07);\n --cc-frame-bg: rgba(255,255,255,.025);\n --cc-frame-border: rgba(255,255,255,.10);\n --cc-text: #e8eaee;\n --cc-muted: #9aa1ad;\n --cc-faint: #c4cad4;\n --cc-panel: #1f2229;\n --cc-panel-border: rgba(255,255,255,.12);\n --cc-accent: #7aa2ff;\n --cc-danger: #ff6b6b;\n --cc-shadow: 0 10px 30px rgba(0,0,0,.45);\n\n --k-import: #6b7280; --k-import-head: #3b3f46; --k-import-bg: #25272c;\n --k-class: #34b27a; --k-class-head: #1d6b4a; --k-class-bg: rgba(52,178,122,.08);\n --k-function: #ec5fa5; --k-function-head: #8f2f62; --k-function-bg: rgba(236,95,165,.09);\n --k-variable: #4b8ef7; --k-variable-head: #23508f; --k-variable-bg: rgba(75,142,247,.09);\n --k-type: #d4a92a; --k-type-head: #6e5714; --k-type-bg: rgba(212,169,42,.08);\n --k-namespace: #9b8cf8; --k-namespace-head: #4d4291; --k-namespace-bg: rgba(155,140,248,.08);\n --k-statement: #7c8799; --k-statement-head: #39414f; --k-statement-bg: rgba(124,135,153,.08);\n\n position: relative; overflow: hidden; width: 100%; height: 100%;\n background-color: var(--cc-bg);\n background-image: radial-gradient(var(--cc-dot) 1.2px, transparent 1.3px);\n color: var(--cc-text);\n font: 13px/1.3 ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif;\n touch-action: none; user-select: none; -webkit-user-select: none; outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.cc-root[data-theme=\"light\"] {\n --cc-bg: #f6f7f9; --cc-dot: rgba(0,0,0,.09);\n --cc-frame-bg: rgba(255,255,255,.7); --cc-frame-border: rgba(0,0,0,.12);\n --cc-text: #1d2129; --cc-muted: #5d6573; --cc-faint: #39404c;\n --cc-panel: #ffffff; --cc-panel-border: rgba(0,0,0,.12); --cc-accent: #3366e6;\n --cc-shadow: 0 10px 30px rgba(20,30,50,.18);\n --k-import: #6b7280; --k-import-head: #4b5058; --k-import-bg: #eceef1;\n --k-class: #1f9963; --k-class-head: #1f9963; --k-class-bg: rgba(31,153,99,.07);\n --k-function: #d63e8c; --k-function-head: #d63e8c; --k-function-bg: rgba(214,62,140,.07);\n --k-variable: #2f6fe0; --k-variable-head: #2f6fe0; --k-variable-bg: rgba(47,111,224,.07);\n --k-type: #b58a0c; --k-type-head: #a87f08; --k-type-bg: rgba(181,138,12,.08);\n --k-namespace: #6f5ce6; --k-namespace-head: #6f5ce6; --k-namespace-bg: rgba(111,92,230,.07);\n --k-statement: #64708a; --k-statement-head: #64708a; --k-statement-bg: rgba(100,112,138,.07);\n}\n.cc-world { position: absolute; left: 0; top: 0; transform-origin: 0 0; }\n.cc-root.cc-moving .cc-world { will-change: transform; }\n.cc-frame { position: absolute; border: 1px solid var(--cc-frame-border); background: var(--cc-frame-bg); border-radius: 18px; }\n.cc-frame-title { position: absolute; left: 20px; top: 12px; font: 600 15px/20px var(--cc-font, \"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace); color: var(--cc-muted); white-space: nowrap; }\n.cc-empty { position: absolute; font-size: 15px; color: var(--cc-muted); white-space: nowrap; }\n\n.cc-block {\n position: absolute; left: 0; top: 0; box-sizing: border-box; border-radius: 10px;\n border: 1.5px solid var(--k); background: var(--kbg); overflow: hidden; contain: layout style;\n cursor: default;\n}\n.cc-block.k-import { --k: var(--k-import); --kh: var(--k-import-head); --kbg: var(--k-import-bg); }\n.cc-block.k-class { --k: var(--k-class); --kh: var(--k-class-head); --kbg: var(--k-class-bg); }\n.cc-block.k-function { --k: var(--k-function); --kh: var(--k-function-head); --kbg: var(--k-function-bg); }\n.cc-block.k-variable { --k: var(--k-variable); --kh: var(--k-variable-head); --kbg: var(--k-variable-bg); }\n.cc-block.k-type { --k: var(--k-type); --kh: var(--k-type-head); --kbg: var(--k-type-bg); }\n.cc-block.k-namespace { --k: var(--k-namespace); --kh: var(--k-namespace-head); --kbg: var(--k-namespace-bg); }\n.cc-block.k-statement { --k: var(--k-statement); --kh: var(--k-statement-head); --kbg: var(--k-statement-bg); }\n.cc-block.k-import, .cc-root:not([data-theme=\"light\"]) .cc-block.k-import { border-color: #555a63; }\n\n.cc-head {\n display: flex; align-items: center; gap: 7px; height: 32px; padding: 0 10px; box-sizing: border-box;\n background: var(--kh); color: #fff; white-space: nowrap; overflow: hidden;\n font: 13px/32px var(--cc-font, \"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);\n}\n.cc-kind { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; opacity: .75; flex: none; }\n.cc-name { font-weight: 700; flex: none; max-width: 70%; overflow: hidden; text-overflow: ellipsis; }\n.cc-detail { opacity: .8; overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 1 1 auto; }\n.cc-tags { font-size: 10px; opacity: .7; flex: none; }\n.cc-exp { width: 6px; height: 6px; border-radius: 50%; background: #fff; opacity: .8; flex: none; }\n.cc-fold {\n flex: none; margin-left: auto; width: 22px; height: 22px; border: 0; border-radius: 6px; padding: 0;\n background: rgba(255,255,255,.14); color: #fff; font-size: 12px; line-height: 22px; cursor: pointer;\n}\n.cc-fold:hover { background: rgba(255,255,255,.28); }\n.cc-prev {\n margin: 0; padding: 5px 12px 0; white-space: pre; overflow: hidden; color: var(--cc-faint); opacity: .5;\n font: 11px/15px var(--cc-font, \"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);\n}\n.cc-more { opacity: .7; font-style: italic; }\n.cc-mini { position: relative; margin: 6px 12px 0; height: 34px; opacity: .45; }\n.cc-mini > i { position: absolute; border-radius: 2px; border: 1px solid var(--cc-faint); background: rgba(255,255,255,.06); }\n.cc-mini > i.k-class { border-color: var(--k-class); } .cc-mini > i.k-function { border-color: var(--k-function); }\n.cc-mini > i.k-variable { border-color: var(--k-variable); } .cc-mini > i.k-type { border-color: var(--k-type); }\n\n.cc-err {\n flex: none; min-width: 18px; height: 18px; padding: 0 5px; box-sizing: border-box; border-radius: 9px;\n background: var(--cc-danger); color: #fff; font: 700 11px/18px ui-sans-serif, system-ui, sans-serif; text-align: center;\n}\n.cc-block.cc-has-error { border-color: var(--cc-danger); }\n.cc-root.lod-2 .cc-err { min-width: 34px; height: 34px; border-radius: 17px; font-size: 22px; line-height: 34px; }\n.cc-rename {\n font: inherit; font-weight: 700; color: #fff; background: rgba(0,0,0,.35); border: 1px solid var(--cc-accent);\n border-radius: 4px; padding: 0 4px; height: 22px; outline: none; user-select: text; -webkit-user-select: text; max-width: 100%;\n}\n.cc-rename.invalid { border-color: var(--cc-danger); }\n.cc-name:has(.cc-rename) { max-width: none; overflow: visible; }\n\n/* merge conflicts */\n.cc-block.cc-conflict { border-color: #f0883e; box-shadow: 0 0 0 2px rgba(240, 136, 62, .35); }\n.cc-conflict-chip {\n flex: none; min-width: 18px; height: 18px; border-radius: 9px; background: #f0883e; color: #1b1300;\n font: 900 13px/18px ui-sans-serif, system-ui, sans-serif; text-align: center;\n}\n.cc-root.lod-2 .cc-conflict-chip { min-width: 34px; height: 34px; border-radius: 17px; font-size: 24px; line-height: 34px; }\n.cc-block.cc-conflict-incoming {\n --k: #f0883e; --kh: #8a4a17; --kbg: rgba(240, 136, 62, .08);\n border-style: dashed; pointer-events: none; opacity: .9;\n}\n.cc-diff-chip.incoming { background: #f0883e; }\n.cc-banner.cc-banner-conflicts { border-color: #f0883e; }\n\n/* changes against a base version (setDiffBase) */\n.cc-block.cc-diff-unchanged { filter: saturate(.3) brightness(.92); }\n.cc-diff-chip {\n flex: none; min-width: 18px; height: 18px; padding: 0 4px; box-sizing: border-box; border-radius: 5px;\n font: 800 13px/18px ui-sans-serif, system-ui, sans-serif; text-align: center; color: #fff;\n}\n.cc-diff-chip.added { background: #2ea043; }\n.cc-diff-chip.changed { background: #d29922; }\n.cc-diff-chip.removed { background: var(--cc-danger); }\n.cc-root.lod-2 .cc-diff-chip { min-width: 34px; height: 34px; border-radius: 8px; font-size: 24px; line-height: 34px; }\n.cc-block.cc-diff-removed {\n --k: var(--cc-danger); --kh: #9c2f2f; --kbg: rgba(255, 107, 107, .10);\n border-style: dashed; pointer-events: none; opacity: .9;\n}\n.cc-root[data-theme=\"light\"] .cc-block.cc-diff-removed { --kh: #d64545; --kbg: rgba(214, 69, 69, .08); }\n.cc-prev.cc-diff-prev { opacity: .95; }\n.cc-diff-prev > span { display: block; margin: 0 -12px; padding: 0 12px; }\n.cc-diff-prev .cc-line-same { opacity: .5; }\n.cc-diff-prev .cc-line-added { background: rgba(46, 160, 67, .30); }\n.cc-diff-prev .cc-line-removed { background: rgba(248, 81, 73, .26); text-decoration: line-through; text-decoration-color: rgba(248, 81, 73, .7); }\n.cc-diff-summary { padding: 0 8px; font-size: 12px; color: var(--cc-muted); white-space: nowrap; }\n\n.cc-block.cc-sel { box-shadow: 0 0 0 2px var(--cc-bg), 0 0 0 4px var(--k); }\n.cc-block.cc-editing { box-shadow: 0 0 0 2px var(--cc-bg), 0 0 0 4px var(--cc-accent); }\n.cc-block.cc-dragging { box-shadow: var(--cc-shadow); opacity: .92; z-index: 9999 !important; }\n.cc-root.has-hl .cc-block.cc-linked { box-shadow: 0 0 0 2px var(--k); }\n\n.cc-edges { position: absolute; left: 0; top: 0; overflow: visible; pointer-events: none; }\n.cc-edge { fill: none; stroke-width: 1.5; opacity: .22; }\n.cc-edge.u-type { stroke-dasharray: 4 4; }\n.cc-root.has-hl .cc-edge { opacity: .07; }\n.cc-root.has-hl .cc-edge.hl { opacity: .95; stroke-width: 2.5; }\n\n/* level of detail when zoomed out */\n.cc-root.lod-1 .cc-tags { visibility: hidden; }\n.cc-root.lod-2 .cc-prev, .cc-root.lod-2 .cc-detail, .cc-root.lod-2 .cc-mini { visibility: hidden; }\n.cc-root.lod-2 .cc-head { font-size: 26px; }\n.cc-root.lod-2 .cc-kind, .cc-root.lod-2 .cc-fold { display: none; }\n.cc-root.lod-2 .cc-frame-title { font-size: 40px; line-height: 44px; top: -8px; }\n\n/* overlays (screen space) */\n.cc-banner {\n position: absolute; left: 50%; top: 12px; transform: translateX(-50%); z-index: 20; display: flex; gap: 10px; align-items: center;\n padding: 8px 12px; border-radius: 10px; background: var(--cc-panel); border: 1px solid var(--cc-danger); box-shadow: var(--cc-shadow);\n font-size: 13px; max-width: calc(100% - 32px);\n}\n.cc-banner[hidden], .cc-bar[hidden], .cc-refs[hidden] { display: none; }\n.cc-toast {\n position: absolute; left: 50%; bottom: 76px; transform: translateX(-50%); z-index: 30; padding: 8px 14px; border-radius: 10px;\n background: var(--cc-panel); border: 1px solid var(--cc-panel-border); box-shadow: var(--cc-shadow); font-size: 13px;\n transition: opacity .2s; pointer-events: none; max-width: calc(100% - 32px);\n}\n.cc-bar {\n position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%); z-index: 20; display: flex; align-items: center; gap: 4px;\n padding: 5px; border-radius: 12px; background: var(--cc-panel); border: 1px solid var(--cc-panel-border); box-shadow: var(--cc-shadow);\n width: max-content; max-width: calc(100% - 24px); box-sizing: border-box; flex-wrap: wrap; justify-content: center;\n}\n.cc-bar .cc-chip { padding: 0 10px 0 6px; font: 600 12px/30px var(--cc-font, \"JetBrains Mono\", ui-monospace, Menlo, monospace); white-space: nowrap; display: flex; align-items: center; gap: 6px; max-width: 220px; overflow: hidden; text-overflow: ellipsis; }\n.cc-bar .cc-chip i { width: 10px; height: 10px; border-radius: 3px; background: var(--k); flex: none; }\n.cc-btn {\n border: 0; background: transparent; color: var(--cc-text); font: 500 13px/30px inherit; font-family: inherit; padding: 0 10px; border-radius: 8px;\n cursor: pointer; white-space: nowrap; min-height: 32px;\n}\n.cc-btn:hover { background: rgba(127,127,127,.16); }\n.cc-btn.danger { color: var(--cc-danger); }\n.cc-btn.primary { background: var(--cc-accent); color: #fff; }\n.cc-sep { width: 1px; height: 20px; background: var(--cc-panel-border); margin: 0 2px; flex: none; }\n.cc-refs {\n position: absolute; left: 50%; bottom: 64px; transform: translateX(-50%); z-index: 21; width: min(360px, calc(100% - 24px)); max-height: 45%;\n overflow: auto; border-radius: 12px; background: var(--cc-panel); border: 1px solid var(--cc-panel-border); box-shadow: var(--cc-shadow); padding: 6px;\n}\n.cc-refs h4 { margin: 8px 8px 4px; font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--cc-muted); }\n.cc-refs button {\n display: flex; width: 100%; align-items: center; gap: 8px; border: 0; background: transparent; color: var(--cc-text); text-align: left;\n padding: 6px 8px; border-radius: 8px; cursor: pointer; font: 12px/18px var(--cc-font, \"JetBrains Mono\", ui-monospace, Menlo, monospace);\n}\n.cc-refs button:hover { background: rgba(127,127,127,.16); }\n.cc-refs button i { width: 10px; height: 10px; border-radius: 3px; background: var(--k); flex: none; }\n.cc-refs button span:last-child { margin-left: auto; color: var(--cc-muted); }\n.cc-refs button:disabled { cursor: default; opacity: .7; }\n.cc-refs button:disabled:hover { background: transparent; }\n.cc-refs .cc-file { color: var(--cc-muted); }\n.cc-refs .cc-error-row { --k: var(--cc-danger); align-items: flex-start; }\n.cc-refs .cc-error-row i { border-radius: 50%; margin-top: 4px; }\n.cc-refs .cc-error-row span:nth-child(2) { white-space: pre-wrap; font-family: ui-sans-serif, system-ui, sans-serif; }\n.cc-refs .cc-error-row span:last-child { white-space: nowrap; }\n.cc-refs .none { padding: 4px 8px 8px; color: var(--cc-muted); font-size: 12px; }\n\n.cc-editor {\n position: absolute; z-index: 25; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden;\n background: var(--cc-panel); border: 1px solid var(--cc-panel-border); box-shadow: var(--cc-shadow);\n}\n.cc-editor-head { display: flex; align-items: center; gap: 8px; padding: 4px 6px 4px 12px; border-bottom: 1px solid var(--cc-panel-border); }\n.cc-editor-head .cc-chip { flex: 1; min-width: 0; font: 600 12px/30px var(--cc-font, \"JetBrains Mono\", ui-monospace, Menlo, monospace); display: flex; align-items: center; gap: 6px; overflow: hidden; white-space: nowrap; }\n.cc-editor-head .cc-chip i { width: 10px; height: 10px; border-radius: 3px; background: var(--k); flex: none; }\n.cc-editor-body { flex: 1; min-height: 0; overflow: auto; user-select: text; -webkit-user-select: text; touch-action: auto; }\n.cc-editor-hint { font-size: 11px; color: var(--cc-muted); white-space: nowrap; }\n@media (max-width: 640px) { .cc-editor-hint { display: none; } }\n";
2
+ export declare const CSS = "\n.cc-root {\n --cc-bg: #15171c;\n --cc-dot: rgba(255,255,255,.07);\n --cc-frame-bg: rgba(255,255,255,.025);\n --cc-frame-border: rgba(255,255,255,.10);\n --cc-text: #e8eaee;\n --cc-muted: #9aa1ad;\n --cc-faint: #c4cad4;\n --cc-panel: #1f2229;\n --cc-panel-border: rgba(255,255,255,.12);\n --cc-accent: #7aa2ff;\n --cc-danger: #ff6b6b;\n --cc-shadow: 0 10px 30px rgba(0,0,0,.45);\n\n --k-import: #6b7280; --k-import-head: #3b3f46; --k-import-bg: #25272c;\n --k-class: #34b27a; --k-class-head: #1d6b4a; --k-class-bg: rgba(52,178,122,.08);\n --k-function: #ec5fa5; --k-function-head: #8f2f62; --k-function-bg: rgba(236,95,165,.09);\n --k-component: #22b8cf; --k-component-head: #146c7a; --k-component-bg: rgba(34,184,207,.09);\n --k-state: #f0883e; --k-state-head: #8a4614; --k-state-bg: rgba(240,136,62,.09);\n --k-variable: #4b8ef7; --k-variable-head: #23508f; --k-variable-bg: rgba(75,142,247,.09);\n --k-type: #d4a92a; --k-type-head: #6e5714; --k-type-bg: rgba(212,169,42,.08);\n --k-namespace: #9b8cf8; --k-namespace-head: #4d4291; --k-namespace-bg: rgba(155,140,248,.08);\n --k-statement: #7c8799; --k-statement-head: #39414f; --k-statement-bg: rgba(124,135,153,.08);\n\n position: relative; overflow: hidden; width: 100%; height: 100%;\n background-color: var(--cc-bg);\n background-image: radial-gradient(var(--cc-dot) 1.2px, transparent 1.3px);\n color: var(--cc-text);\n font: 13px/1.3 ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif;\n touch-action: none; user-select: none; -webkit-user-select: none; outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.cc-root[data-theme=\"light\"] {\n --cc-bg: #f6f7f9; --cc-dot: rgba(0,0,0,.09);\n --cc-frame-bg: rgba(255,255,255,.7); --cc-frame-border: rgba(0,0,0,.12);\n --cc-text: #1d2129; --cc-muted: #5d6573; --cc-faint: #39404c;\n --cc-panel: #ffffff; --cc-panel-border: rgba(0,0,0,.12); --cc-accent: #3366e6;\n --cc-shadow: 0 10px 30px rgba(20,30,50,.18);\n --k-import: #6b7280; --k-import-head: #4b5058; --k-import-bg: #eceef1;\n --k-class: #1f9963; --k-class-head: #1f9963; --k-class-bg: rgba(31,153,99,.07);\n --k-function: #d63e8c; --k-function-head: #d63e8c; --k-function-bg: rgba(214,62,140,.07);\n --k-component: #0f8fa3; --k-component-head: #0f8fa3; --k-component-bg: rgba(15,143,163,.07);\n --k-state: #d4691c; --k-state-head: #d4691c; --k-state-bg: rgba(212,105,28,.07);\n --k-variable: #2f6fe0; --k-variable-head: #2f6fe0; --k-variable-bg: rgba(47,111,224,.07);\n --k-type: #b58a0c; --k-type-head: #a87f08; --k-type-bg: rgba(181,138,12,.08);\n --k-namespace: #6f5ce6; --k-namespace-head: #6f5ce6; --k-namespace-bg: rgba(111,92,230,.07);\n --k-statement: #64708a; --k-statement-head: #64708a; --k-statement-bg: rgba(100,112,138,.07);\n}\n.cc-world { position: absolute; left: 0; top: 0; transform-origin: 0 0; }\n.cc-root.cc-moving .cc-world { will-change: transform; }\n.cc-frame { position: absolute; border: 1px solid var(--cc-frame-border); background: var(--cc-frame-bg); border-radius: 18px; }\n.cc-frame-title { position: absolute; left: 20px; top: 12px; font: 600 15px/20px var(--cc-font, \"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace); color: var(--cc-muted); white-space: nowrap; }\n.cc-empty { position: absolute; font-size: 15px; color: var(--cc-muted); white-space: nowrap; }\n\n.cc-block {\n position: absolute; left: 0; top: 0; box-sizing: border-box; border-radius: 10px;\n border: 1.5px solid var(--k); background: var(--kbg); overflow: hidden; contain: layout style;\n cursor: default;\n}\n.cc-block.k-import { --k: var(--k-import); --kh: var(--k-import-head); --kbg: var(--k-import-bg); }\n.cc-block.k-class { --k: var(--k-class); --kh: var(--k-class-head); --kbg: var(--k-class-bg); }\n.cc-block.k-function { --k: var(--k-function); --kh: var(--k-function-head); --kbg: var(--k-function-bg); }\n.cc-block.k-component { --k: var(--k-component); --kh: var(--k-component-head); --kbg: var(--k-component-bg); }\n.cc-block.k-state { --k: var(--k-state); --kh: var(--k-state-head); --kbg: var(--k-state-bg); }\n.cc-block.k-variable { --k: var(--k-variable); --kh: var(--k-variable-head); --kbg: var(--k-variable-bg); }\n.cc-block.k-type { --k: var(--k-type); --kh: var(--k-type-head); --kbg: var(--k-type-bg); }\n.cc-block.k-namespace { --k: var(--k-namespace); --kh: var(--k-namespace-head); --kbg: var(--k-namespace-bg); }\n.cc-block.k-statement { --k: var(--k-statement); --kh: var(--k-statement-head); --kbg: var(--k-statement-bg); }\n.cc-block.k-import, .cc-root:not([data-theme=\"light\"]) .cc-block.k-import { border-color: #555a63; }\n\n.cc-head {\n display: flex; align-items: center; gap: 7px; height: 32px; padding: 0 10px; box-sizing: border-box;\n background: var(--kh); color: #fff; white-space: nowrap; overflow: hidden;\n font: 13px/32px var(--cc-font, \"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);\n}\n.cc-kind { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; opacity: .75; flex: none; }\n.cc-name { font-weight: 700; flex: none; max-width: 70%; overflow: hidden; text-overflow: ellipsis; }\n.cc-detail { opacity: .8; overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 1 1 auto; }\n.cc-tags { font-size: 10px; opacity: .7; flex: none; }\n.cc-exp { width: 6px; height: 6px; border-radius: 50%; background: #fff; opacity: .8; flex: none; }\n.cc-fold {\n flex: none; margin-left: auto; width: 22px; height: 22px; border: 0; border-radius: 6px; padding: 0;\n background: rgba(255,255,255,.14); color: #fff; font-size: 12px; line-height: 22px; cursor: pointer;\n}\n.cc-fold:hover { background: rgba(255,255,255,.28); }\n.cc-prev {\n margin: 0; padding: 5px 12px 0; white-space: pre; overflow: hidden; color: var(--cc-faint); opacity: .5;\n font: 11px/15px var(--cc-font, \"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);\n}\n.cc-more { opacity: .7; font-style: italic; }\n.cc-mini { position: relative; margin: 6px 12px 0; height: 34px; opacity: .45; }\n.cc-mini > i { position: absolute; border-radius: 2px; border: 1px solid var(--cc-faint); background: rgba(255,255,255,.06); }\n.cc-mini > i.k-class { border-color: var(--k-class); } .cc-mini > i.k-function { border-color: var(--k-function); }\n.cc-mini > i.k-component { border-color: var(--k-component); } .cc-mini > i.k-state { border-color: var(--k-state); }\n.cc-mini > i.k-variable { border-color: var(--k-variable); } .cc-mini > i.k-type { border-color: var(--k-type); }\n\n.cc-err {\n flex: none; min-width: 18px; height: 18px; padding: 0 5px; box-sizing: border-box; border-radius: 9px;\n background: var(--cc-danger); color: #fff; font: 700 11px/18px ui-sans-serif, system-ui, sans-serif; text-align: center;\n}\n.cc-block.cc-has-error { border-color: var(--cc-danger); }\n.cc-root.lod-2 .cc-err { min-width: 34px; height: 34px; border-radius: 17px; font-size: 22px; line-height: 34px; }\n.cc-rename {\n font: inherit; font-weight: 700; color: #fff; background: rgba(0,0,0,.35); border: 1px solid var(--cc-accent);\n border-radius: 4px; padding: 0 4px; height: 22px; outline: none; user-select: text; -webkit-user-select: text; max-width: 100%;\n}\n.cc-rename.invalid { border-color: var(--cc-danger); }\n.cc-name:has(.cc-rename) { max-width: none; overflow: visible; }\n\n/* merge conflicts */\n.cc-block.cc-conflict { border-color: #f0883e; box-shadow: 0 0 0 2px rgba(240, 136, 62, .35); }\n.cc-conflict-chip {\n flex: none; min-width: 18px; height: 18px; border-radius: 9px; background: #f0883e; color: #1b1300;\n font: 900 13px/18px ui-sans-serif, system-ui, sans-serif; text-align: center;\n}\n.cc-root.lod-2 .cc-conflict-chip { min-width: 34px; height: 34px; border-radius: 17px; font-size: 24px; line-height: 34px; }\n.cc-block.cc-conflict-incoming {\n --k: #f0883e; --kh: #8a4a17; --kbg: rgba(240, 136, 62, .08);\n border-style: dashed; pointer-events: none; opacity: .9;\n}\n.cc-diff-chip.incoming { background: #f0883e; }\n.cc-banner.cc-banner-conflicts { border-color: #f0883e; }\n\n/* changes against a base version (setDiffBase) */\n.cc-block.cc-diff-unchanged { filter: saturate(.3) brightness(.92); }\n.cc-diff-chip {\n flex: none; min-width: 18px; height: 18px; padding: 0 4px; box-sizing: border-box; border-radius: 5px;\n font: 800 13px/18px ui-sans-serif, system-ui, sans-serif; text-align: center; color: #fff;\n}\n.cc-diff-chip.added { background: #2ea043; }\n.cc-diff-chip.changed { background: #d29922; }\n.cc-diff-chip.removed { background: var(--cc-danger); }\n.cc-root.lod-2 .cc-diff-chip { min-width: 34px; height: 34px; border-radius: 8px; font-size: 24px; line-height: 34px; }\n.cc-block.cc-diff-removed {\n --k: var(--cc-danger); --kh: #9c2f2f; --kbg: rgba(255, 107, 107, .10);\n border-style: dashed; pointer-events: none; opacity: .9;\n}\n.cc-root[data-theme=\"light\"] .cc-block.cc-diff-removed { --kh: #d64545; --kbg: rgba(214, 69, 69, .08); }\n.cc-prev.cc-diff-prev { opacity: .95; }\n.cc-diff-prev > span { display: block; margin: 0 -12px; padding: 0 12px; }\n.cc-diff-prev .cc-line-same { opacity: .5; }\n.cc-diff-prev .cc-line-added { background: rgba(46, 160, 67, .30); }\n.cc-diff-prev .cc-line-removed { background: rgba(248, 81, 73, .26); text-decoration: line-through; text-decoration-color: rgba(248, 81, 73, .7); }\n.cc-diff-summary { padding: 0 8px; font-size: 12px; color: var(--cc-muted); white-space: nowrap; }\n\n.cc-block.cc-sel { box-shadow: 0 0 0 2px var(--cc-bg), 0 0 0 4px var(--k); }\n.cc-block.cc-editing { box-shadow: 0 0 0 2px var(--cc-bg), 0 0 0 4px var(--cc-accent); }\n.cc-block.cc-dragging { box-shadow: var(--cc-shadow); opacity: .92; z-index: 9999 !important; }\n.cc-root.has-hl .cc-block.cc-linked { box-shadow: 0 0 0 2px var(--k); }\n\n.cc-edges { position: absolute; left: 0; top: 0; overflow: visible; pointer-events: none; }\n.cc-edge { fill: none; stroke-width: 1.5; opacity: .22; }\n.cc-edge.u-type { stroke-dasharray: 4 4; }\n.cc-root.has-hl .cc-edge { opacity: .07; }\n.cc-root.has-hl .cc-edge.hl { opacity: .95; stroke-width: 2.5; }\n\n/* level of detail when zoomed out */\n.cc-root.lod-1 .cc-tags { visibility: hidden; }\n.cc-root.lod-2 .cc-prev, .cc-root.lod-2 .cc-detail, .cc-root.lod-2 .cc-mini { visibility: hidden; }\n.cc-root.lod-2 .cc-head { font-size: 26px; }\n.cc-root.lod-2 .cc-kind, .cc-root.lod-2 .cc-fold { display: none; }\n.cc-root.lod-2 .cc-frame-title { font-size: 40px; line-height: 44px; top: -8px; }\n\n/* overlays (screen space) */\n.cc-banner {\n position: absolute; left: 50%; top: 12px; transform: translateX(-50%); z-index: 20; display: flex; gap: 10px; align-items: center;\n padding: 8px 12px; border-radius: 10px; background: var(--cc-panel); border: 1px solid var(--cc-danger); box-shadow: var(--cc-shadow);\n font-size: 13px; max-width: calc(100% - 32px);\n}\n.cc-banner[hidden], .cc-bar[hidden], .cc-refs[hidden] { display: none; }\n.cc-toast {\n position: absolute; left: 50%; bottom: 76px; transform: translateX(-50%); z-index: 30; padding: 8px 14px; border-radius: 10px;\n background: var(--cc-panel); border: 1px solid var(--cc-panel-border); box-shadow: var(--cc-shadow); font-size: 13px;\n transition: opacity .2s; pointer-events: none; max-width: calc(100% - 32px);\n}\n.cc-bar {\n position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%); z-index: 20; display: flex; align-items: center; gap: 4px;\n padding: 5px; border-radius: 12px; background: var(--cc-panel); border: 1px solid var(--cc-panel-border); box-shadow: var(--cc-shadow);\n width: max-content; max-width: calc(100% - 24px); box-sizing: border-box; flex-wrap: wrap; justify-content: center;\n}\n.cc-bar .cc-chip { padding: 0 10px 0 6px; font: 600 12px/30px var(--cc-font, \"JetBrains Mono\", ui-monospace, Menlo, monospace); white-space: nowrap; display: flex; align-items: center; gap: 6px; max-width: 220px; overflow: hidden; text-overflow: ellipsis; }\n.cc-bar .cc-chip i { width: 10px; height: 10px; border-radius: 3px; background: var(--k); flex: none; }\n.cc-btn {\n border: 0; background: transparent; color: var(--cc-text); font: 500 13px/30px inherit; font-family: inherit; padding: 0 10px; border-radius: 8px;\n cursor: pointer; white-space: nowrap; min-height: 32px;\n}\n.cc-btn:hover { background: rgba(127,127,127,.16); }\n.cc-btn.danger { color: var(--cc-danger); }\n.cc-btn.primary { background: var(--cc-accent); color: #fff; }\n.cc-language { padding: 0 8px; font: 600 11px/30px var(--cc-font, \"JetBrains Mono\", ui-monospace, Menlo, monospace); color: var(--cc-muted); white-space: nowrap; }\n.cc-sep { width: 1px; height: 20px; background: var(--cc-panel-border); margin: 0 2px; flex: none; }\n.cc-refs {\n position: absolute; left: 50%; bottom: 64px; transform: translateX(-50%); z-index: 21; width: min(360px, calc(100% - 24px)); max-height: 45%;\n overflow: auto; border-radius: 12px; background: var(--cc-panel); border: 1px solid var(--cc-panel-border); box-shadow: var(--cc-shadow); padding: 6px;\n}\n.cc-refs h4 { margin: 8px 8px 4px; font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--cc-muted); }\n.cc-refs button {\n display: flex; width: 100%; align-items: center; gap: 8px; border: 0; background: transparent; color: var(--cc-text); text-align: left;\n padding: 6px 8px; border-radius: 8px; cursor: pointer; font: 12px/18px var(--cc-font, \"JetBrains Mono\", ui-monospace, Menlo, monospace);\n}\n.cc-refs button:hover { background: rgba(127,127,127,.16); }\n.cc-refs button i { width: 10px; height: 10px; border-radius: 3px; background: var(--k); flex: none; }\n.cc-refs button span:last-child { margin-left: auto; color: var(--cc-muted); }\n.cc-refs button:disabled { cursor: default; opacity: .7; }\n.cc-refs button:disabled:hover { background: transparent; }\n.cc-refs .cc-file { color: var(--cc-muted); }\n.cc-refs .cc-error-row { --k: var(--cc-danger); align-items: flex-start; }\n.cc-refs .cc-error-row i { border-radius: 50%; margin-top: 4px; }\n.cc-refs .cc-error-row span:nth-child(2) { white-space: pre-wrap; font-family: ui-sans-serif, system-ui, sans-serif; }\n.cc-refs .cc-error-row span:last-child { white-space: nowrap; }\n.cc-refs .none { padding: 4px 8px 8px; color: var(--cc-muted); font-size: 12px; }\n\n.cc-editor {\n position: absolute; z-index: 25; display: flex; flex-direction: column; border-radius: 12px; overflow: hidden;\n background: var(--cc-panel); border: 1px solid var(--cc-panel-border); box-shadow: var(--cc-shadow);\n}\n.cc-editor-head { display: flex; align-items: center; gap: 8px; padding: 4px 6px 4px 12px; border-bottom: 1px solid var(--cc-panel-border); }\n.cc-editor-head .cc-chip { flex: 1; min-width: 0; font: 600 12px/30px var(--cc-font, \"JetBrains Mono\", ui-monospace, Menlo, monospace); display: flex; align-items: center; gap: 6px; overflow: hidden; white-space: nowrap; }\n.cc-editor-head .cc-chip i { width: 10px; height: 10px; border-radius: 3px; background: var(--k); flex: none; }\n.cc-editor-body { flex: 1; min-height: 0; overflow: auto; user-select: text; -webkit-user-select: text; touch-action: auto; }\n.cc-editor-hint { font-size: 11px; color: var(--cc-muted); white-space: nowrap; }\n@media (max-width: 640px) { .cc-editor-hint { display: none; } }\n";
3
3
  export declare function injectStyles(doc?: Document): void;
@@ -0,0 +1,3 @@
1
+ import ts from 'typescript';
2
+ /** The expression inside parentheses and type assertions: `(x as Y)` → `x`. */
3
+ export declare function unwrap(e: ts.Expression): ts.Expression;
@@ -1,5 +1,5 @@
1
1
  /** What a block represents. Each kind has its own color on the canvas. */
2
- export type BlockKind = 'import' | 'class' | 'function' | 'variable' | 'type' | 'namespace' | 'statement';
2
+ export type BlockKind = 'import' | 'class' | 'function' | 'component' | 'state' | 'variable' | 'type' | 'namespace' | 'statement';
3
3
  /** Where new child code goes when a block is used as a container. */
4
4
  export type InsertStyle = 'class' | 'body' | null;
5
5
  export interface Block {
@@ -32,7 +32,8 @@ export interface Block {
32
32
  /** Indentation of the line the block starts on. */
33
33
  indent: string;
34
34
  }
35
- export type UsageKind = 'call' | 'read' | 'write' | 'type';
35
+ /** `render`: a component used as a JSX tag, `<Cart />`. */
36
+ export type UsageKind = 'call' | 'render' | 'read' | 'write' | 'type';
36
37
  /** A line on the canvas: code in `from` uses the declaration in `to`. */
37
38
  export interface Usage {
38
39
  from: string;
@@ -75,6 +76,18 @@ export interface FileModel {
75
76
  diagnostics: CodeDiagnostic[];
76
77
  /** Indentation unit detected in the file. */
77
78
  indentUnit: string;
79
+ language: FileLanguage;
80
+ }
81
+ export type LanguageLabel = 'JS' | 'TS' | 'React JS' | 'React TS' | 'React JSX' | 'React TSX';
82
+ /** What the file is written in. React mode recognizes components, state and render links. */
83
+ export interface FileLanguage {
84
+ script: 'js' | 'ts';
85
+ /** A `.jsx`/`.tsx` file, or a file with JSX in it. */
86
+ hasJsx: boolean;
87
+ /** React mode: the file has JSX or imports `react`, `react-dom` or `react-native`. */
88
+ isReact: boolean;
89
+ /** Shown on the canvas toolbar. */
90
+ label: LanguageLabel;
78
91
  }
79
92
  export interface Point {
80
93
  x: number;
@@ -0,0 +1,45 @@
1
+ import ts from 'typescript';
2
+ /**
3
+ * A TypeScript config file of the folder (`tsconfig.json`, `tsconfig.app.json`, `jsconfig.json`).
4
+ * Path aliases, `baseUrl` and module resolution decide which file an import points at.
5
+ */
6
+ export interface ConfigFile {
7
+ /** Project path, e.g. `tsconfig.json` or `packages/web/tsconfig.app.json`. */
8
+ path: string;
9
+ text: string;
10
+ /** Configs it `extends`, by project path. Only relative `extends` are followed. */
11
+ extendedFiles?: Record<string, string>;
12
+ }
13
+ /**
14
+ * The folder's TypeScript settings. In a monorepo every package can have its own config: a file
15
+ * uses the nearest config above it that includes it (`files`, `include`, `exclude`), as in VS Code.
16
+ */
17
+ export interface Workspace {
18
+ configFiles: ConfigFile[];
19
+ /**
20
+ * `package.json` files by project path. Packages of the workspace import each other by name
21
+ * (`@acme/utils`); without `node_modules`, the name is resolved to the package's source.
22
+ */
23
+ packageFiles?: Record<string, string>;
24
+ }
25
+ /** One config file, read: its options, and which of the project's files it includes. */
26
+ export interface ParsedConfig {
27
+ path: string;
28
+ /** Folder of the config, `''` at the root, else ending in `/`. */
29
+ folder: string;
30
+ options: ts.CompilerOptions;
31
+ includes(projectPath: string): boolean;
32
+ }
33
+ /** Read a config file. `projectPaths` are the project's files, for its `include` and `exclude` patterns. */
34
+ export declare function parseConfig(config: ConfigFile, projectPaths: string[]): ParsedConfig | null;
35
+ /**
36
+ * The config a file uses: the nearest one above it that includes it; failing that, the nearest one
37
+ * above it (a file left out still gets its package's aliases). Null: no config is above it.
38
+ */
39
+ export declare function configForFile(projectPath: string, configs: ParsedConfig[]): ParsedConfig | null;
40
+ /**
41
+ * Path aliases that point each workspace package's name at its source: the file its `package.json`
42
+ * names (`types`, `exports`, `module`, `main`), its TypeScript source when that is a build output
43
+ * (`dist/index.js` → `src/index.ts`), or `src/index.ts`.
44
+ */
45
+ export declare function workspacePackagePaths(packageFiles: Record<string, string>, hasFile: (projectPath: string) => boolean): Record<string, string[]>;
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@codecanvas-ide/engine",
3
- "version": "0.1.0",
4
- "description": "The canvas of CodeCanvas: TypeScript files as an editable diagram of blocks, with usage links, references across files, rename, errors, diffs and merge conflicts.",
3
+ "version": "0.3.0",
4
+ "description": "The canvas of CodeCanvas: TypeScript and JavaScript files as an editable diagram of blocks, with usage links, references across files, rename, errors, diffs and merge conflicts.",
5
5
  "keywords": [
6
6
  "typescript",
7
+ "javascript",
8
+ "react",
7
9
  "code-visualization",
8
10
  "diagram",
9
11
  "canvas",