@codecanvas-ide/engine 0.2.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.
@@ -248,6 +248,7 @@ export declare class CodeCanvas {
248
248
  restoreCamera?: boolean;
249
249
  }): void;
250
250
  private renderBar;
251
+ private languageLabelParts;
251
252
  private toggleRefs;
252
253
  private showErrorsOf;
253
254
  private diagnosticsInside;
@@ -29,7 +29,7 @@ export interface StackRule {
29
29
  export declare const STACK_RULES: StackRule[];
30
30
  /** What a block knows about its siblings, read from its own code. */
31
31
  export interface SiblingFacts {
32
- /** 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. */
33
33
  calls: string[];
34
34
  /** Every sibling it names (calls included). */
35
35
  uses: Set<string>;
@@ -23,6 +23,7 @@ export declare function detectIndentUnit(text: string): string;
23
23
  declare class Builder {
24
24
  private sf;
25
25
  private text;
26
+ private isReact;
26
27
  roots: Block[];
27
28
  byId: Map<string, Block>;
28
29
  /**
@@ -34,13 +35,17 @@ declare class Builder {
34
35
  private rangeToBlock;
35
36
  /** Every name a usage could resolve to. Other identifiers skip the (slow) checker. */
36
37
  readonly names: Set<string>;
37
- 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);
38
41
  collectStatements(stmts: readonly ts.Statement[], parent: Block | null, depth: number, declarationsOnly?: boolean): void;
39
42
  private addImport;
40
43
  private addFunctionLike;
41
44
  private addClass;
42
45
  private collectMembers;
43
46
  private addVariableStatement;
47
+ /** `const [count, setCount] = useState(0)`: a state block named `count`; using `setCount` writes it. */
48
+ private addState;
44
49
  private addVariable;
45
50
  private add;
46
51
  /** Include doc comments directly above the node (no blank line in between). */
@@ -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,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;
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@codecanvas-ide/engine",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
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
7
  "javascript",
8
+ "react",
8
9
  "code-visualization",
9
10
  "diagram",
10
11
  "canvas",