= {
+ images: "https://framerusercontent.com/images/GfGkADagM4KEibNcIiRUWlfrR0.jpg",
+ videos: "https://framerusercontent.com/assets/MLWPbW1dUQawJLhhun3dBwpgJak.mp4",
+ audio: "https://framerusercontent.com/assets/8w3IUatLX9a5JVJ6XPCVuHi94.mp3",
+}
+```
+
+Recommended pattern for file control defaults:
+
+```tsx
+function MyComponent(props) {
+ // CORRECT: Set file defaults through parameter destructuring
+ const {
+ imageFile = "https://framerusercontent.com/images/GfGkADagM4KEibNcIiRUWlfrR0.jpg",
+ videoFile = "https://framerusercontent.com/assets/MLWPbW1dUQawJLhhun3dBwpgJak.mp4",
+ audioFile = "https://framerusercontent.com/assets/8w3IUatLX9a5JVJ6XPCVuHi94.mp3",
+ } = props
+
+ return (
+
+
+
+
+
+ )
+}
+
+addPropertyControls(MyComponent, {
+ imageFile: {
+ type: ControlType.File,
+ allowedFileTypes: ["jpg", "jpeg", "png", "gif", "webp", "svg"],
+ },
+ // Additional file controls...
+})
+```
diff --git a/packages/code-link-cli/skills/references/PROPERTY_CONTROL_TYPES.md b/packages/code-link-cli/skills/references/PROPERTY_CONTROL_TYPES.md
new file mode 100644
index 000000000..91e44766d
--- /dev/null
+++ b/packages/code-link-cli/skills/references/PROPERTY_CONTROL_TYPES.md
@@ -0,0 +1,488 @@
+# Framer Property Control Types
+
+TypeScript interfaces for all Framer property control types. For usage documentation and examples, see [Property Controls](PROPERTY_CONTROLS.md).
+
+## Base Control Description
+
+All control descriptions extend this base interface:
+
+```typescript
+export interface BaseControlDescription {
+ title?: string;
+ description?: string;
+ hidden?: ((props: P, rootProps: any) => boolean) | boolean;
+}
+
+export interface WithOptional {
+ optional?: boolean;
+}
+```
+
+## Control Type Interfaces
+
+### Boolean Control
+
+```typescript
+export interface BooleanControlDescription
+ extends BaseControlDescription
, WithOptional {
+ type: ControlType.Boolean;
+ defaultValue?: boolean;
+ /** @deprecated */
+ disabledTitle?: string;
+ /** @deprecated */
+ enabledTitle?: string;
+}
+```
+
+### Number Control
+
+```typescript
+export interface NumberControlDescription
+ extends BaseControlDescription
, WithOptional {
+ type: ControlType.Number;
+ defaultValue?: number;
+ max?: number;
+ min?: number;
+ unit?: string;
+ step?: number;
+ displayStepper?: boolean;
+}
+```
+
+### String Control
+
+```typescript
+export interface StringControlDescription
+ extends BaseControlDescription
, WithOptional {
+ type: ControlType.String;
+ defaultValue?: string;
+ placeholder?: string;
+ obscured?: boolean;
+ displayTextArea?: boolean;
+ preventLocalization?: boolean;
+}
+```
+
+### Enum Control
+
+```typescript
+export interface EnumControlDescription<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.Enum;
+ defaultValue?: string | boolean | number | null;
+ options: (string | boolean | number | null)[];
+ optionTitles?: string[];
+ displaySegmentedControl?: boolean;
+ segmentedControlDirection?: "horizontal" | "vertical";
+}
+```
+
+### Color Control
+
+```typescript
+export interface ColorControlDescription
+ extends BaseControlDescription
, WithOptional {
+ type: ControlType.Color;
+ defaultValue?: string;
+}
+```
+
+### ResponsiveImage Control
+
+```typescript
+export interface ResponsiveImageControlDescription<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.ResponsiveImage;
+}
+```
+
+### File Control
+
+```typescript
+export type AllowedFileTypes = readonly string[];
+
+export interface FileControlDescription<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.File;
+ allowedFileTypes: AllowedFileTypes;
+}
+```
+
+### Slot Control
+
+```typescript
+export interface SlotControlDescription<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.Slot;
+ maxCount?: number;
+}
+```
+
+### Array Control
+
+```typescript
+export interface ArrayControlDescription<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.Array;
+ control: ArrayItemControlDescription
;
+ minCount?: number;
+ maxCount?: number;
+ defaultValue?: any[];
+}
+```
+
+### Object Control
+
+```typescript
+export type ObjectControlIcon =
+ | "object"
+ | "effect"
+ | "color"
+ | "interaction"
+ | "boolean";
+
+export interface ObjectControlDescription
+ extends BaseControlDescription
, WithOptional {
+ type: ControlType.Object;
+ controls: { [key: string]: ObjectPropertyControlDescription };
+ defaultValue?: { [key: string]: any };
+ buttonTitle?: string;
+ icon?: ObjectControlIcon;
+}
+```
+
+### Event Handler Control
+
+```typescript
+export interface EventHandlerControlDescription<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.EventHandler;
+}
+```
+
+### Transition Control
+
+```typescript
+import type { Transition } from "framer-motion";
+
+export interface TransitionControlDescription<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.Transition;
+ defaultValue?: null | Transition;
+}
+```
+
+### BoxShadow Control
+
+```typescript
+export interface BoxShadowControlDescription<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.BoxShadow;
+ defaultValue?: string | readonly BoxShadow[];
+}
+```
+
+### Link Control
+
+```typescript
+export interface LinkControlDescription<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.Link;
+ defaultValue?: string;
+}
+```
+
+### Date Control
+
+```typescript
+export interface DateControlDescription
+ extends BaseControlDescription
, WithOptional {
+ type: ControlType.Date;
+ displayTime?: boolean;
+ defaultValue?: string;
+}
+```
+
+### Border Control
+
+```typescript
+export type BorderStyle = "solid" | "dashed" | "dotted" | "double";
+
+export interface Border {
+ borderColor?: string;
+ borderStyle?: BorderStyle;
+ borderWidth?: number;
+ borderTopWidth?: number;
+ borderLeftWidth?: number;
+ borderRightWidth?: number;
+ borderBottomWidth?: number;
+}
+
+export interface BorderControlDescription
+ extends BaseControlDescription
, WithOptional {
+ type: ControlType.Border;
+ defaultValue?: Border;
+}
+```
+
+### Cursor Control
+
+```typescript
+export interface CursorControlDescription<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.Cursor;
+ defaultValue?: string;
+}
+```
+
+### Padding Control
+
+```typescript
+export interface PaddingControlDescription<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.Padding;
+ defaultValue?: string;
+}
+```
+
+### Border Radius Control
+
+```typescript
+export interface BorderRadiusControlDescription<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.BorderRadius;
+ defaultValue?: string;
+}
+```
+
+### Gap Control
+
+```typescript
+export interface GapControlDescription<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.Gap;
+ defaultValue?: string;
+}
+```
+
+### Tracking ID Control
+
+```typescript
+export interface TrackingIdControlDescription<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.TrackingId;
+ defaultValue?: string;
+}
+```
+
+### Font Control
+
+```typescript
+interface FontControlDescriptionBase<
+ P = any,
+> extends BaseControlDescription
{
+ type: ControlType.Font;
+ controls?: "basic" | "extended";
+ displayTextAlignment?: boolean;
+ displayFontSize?: boolean;
+ defaultValue?: FontControlDefaultValueBase;
+}
+
+interface FontControlDescriptionSansSerif<
+ P = any,
+> extends FontControlDescriptionBase
{
+ defaultFontType?: "sans-serif";
+ defaultValue?: FontControlDefaultValueWithVariant;
+}
+
+interface FontControlDescriptionSerif<
+ P = any,
+> extends FontControlDescriptionBase
{
+ defaultFontType?: "serif";
+ defaultValue?: FontControlDefaultValueBase;
+}
+
+interface FontControlDescriptionMonospace<
+ P = any,
+> extends FontControlDescriptionBase
{
+ defaultFontType?: "monospace";
+ defaultValue?: FontControlDefaultValueBase;
+}
+
+export type FontControlDescription
=
+ | FontControlDescriptionSansSerif
+ | FontControlDescriptionSerif
+ | FontControlDescriptionMonospace
;
+
+interface FontControlDefaultValueBase {
+ textAlign?: "left" | "right" | "center" | "justify";
+ fontSize?: string | number;
+ letterSpacing?: string | number;
+ lineHeight?: string | number;
+}
+
+interface FontControlDefaultValueWithVariant extends FontControlDefaultValueBase {
+ variant?: FramerFontVariant;
+}
+
+export const framerFontVariants = [
+ "Regular",
+ "Thin",
+ "Extra Light",
+ "Light",
+ "Medium",
+ "Semibold",
+ "Bold",
+ "Extra Bold",
+ "Black",
+ "Thin Italic",
+ "Extra Light Italic",
+ "Light Italic",
+ "Italic",
+ "Medium Italic",
+ "Semibold Italic",
+ "Bold Italic",
+ "Extra Bold Italic",
+ "Black Italic",
+ "Regular Italic",
+ "Variable",
+ "Variable Italic",
+] as const;
+
+export type FramerFontVariant = (typeof framerFontVariants)[number];
+```
+
+## Composite Types
+
+### All Control Types
+
+```typescript
+export type ControlDescription
=
+ | NumberControlDescription
+ | EnumControlDescription
+ | BooleanControlDescription
+ | StringControlDescription
+ | ColorControlDescription
+ | ResponsiveImageControlDescription
+ | FileControlDescription
+ | SlotControlDescription
+ | ArrayControlDescription
+ | EventHandlerControlDescription
+ | TransitionControlDescription
+ | BoxShadowControlDescription
+ | LinkControlDescription
+ | DateControlDescription
+ | ObjectControlDescription
+ | FontControlDescription
+ | BorderControlDescription
+ | CursorControlDescription
+ | PaddingControlDescription
+ | BorderRadiusControlDescription
+ | GapControlDescription
+ | TrackingIdControlDescription
;
+```
+
+### Property Controls
+
+```typescript
+export type PropertyControls = {
+ [K in keyof ComponentProps]?: ControlDescription>;
+};
+```
+
+## Associated Methods and Types
+
+### addPropertyControls
+
+```typescript
+export declare function addPropertyControls(
+ component:
+ | React.ComponentType
+ | React.ForwardRefExoticComponent,
+ propertyControls: PropertyControls,
+): void;
+```
+
+### addFonts
+
+```typescript
+export declare function addFonts(
+ component: React.ComponentType,
+ fonts: any[],
+ flags?: { supportsExplicitInterCodegen?: boolean },
+): void;
+```
+
+### Data API
+
+```typescript
+export declare const Data: {
+ (initial?: Partial | object): T;
+};
+```
+
+### Renderer Detection APIs
+
+```typescript
+export declare type RenderTarget = RenderTargetName;
+
+export declare const RenderTarget: {
+ canvas: RenderTargetName;
+ export: RenderTargetName;
+ thumbnail: RenderTargetName;
+ preview: RenderTargetName;
+ current: () => RenderTargetName;
+ hasRestrictions: () => boolean;
+};
+
+/** Check if executed in a Framer Canvas or Export Canvas environment */
+export declare function isStaticRenderer(): boolean;
+
+/** Hook to check if in a static renderer (Canvas or Export) */
+export declare function useIsStaticRenderer(): boolean;
+
+/** Hook to observe data changes */
+export declare function useObserveData(): boolean;
+```
+
+### Color Interface and Utilities
+
+```typescript
+export interface Color {
+ r: number;
+ g: number;
+ b: number;
+ h: number;
+ s: number;
+ l: number;
+ a: number;
+ roundA: number;
+ format: ColorFormat;
+ initialValue?: string;
+ isValid?: boolean;
+ mix: any;
+ toValue: () => string;
+}
+
+export enum ColorFormat {
+ RGB = "rgb",
+ HSL = "hsl",
+ HSV = "hsv",
+ HEX = "hex",
+ NAME = "name",
+}
+```
diff --git a/packages/code-link-cli/src/helpers/installer.ts b/packages/code-link-cli/src/helpers/installer.ts
index 259e04010..abc435a0d 100644
--- a/packages/code-link-cli/src/helpers/installer.ts
+++ b/packages/code-link-cli/src/helpers/installer.ts
@@ -8,6 +8,7 @@ import path from "path"
import ts from "typescript"
import { extractImports } from "../utils/imports.ts"
import { debug, error, warn } from "../utils/logging.ts"
+import { installSkills } from "./skills.ts"
export interface InstallerConfig {
projectDir: string
@@ -40,6 +41,7 @@ const REACT_DOM_TYPES_VERSION = "18.3.1"
const CORE_LIBRARIES = ["framer-motion", "framer"]
const JSON_EXTENSION_REGEX = /\.json$/i
+
/**
* Packages that are officially supported for type acquisition.
* Use --unsupported-npm flag to allow other packages.
@@ -173,6 +175,7 @@ export class Installer {
this.ensurePrettierConfig(),
this.ensureFramerDeclarations(),
this.ensurePackageJson(),
+ this.ensureSkills(),
])
// Fire-and-forget type installation - don't block initialization
@@ -380,6 +383,10 @@ declare module "*.json"
}
}
+ private async ensureSkills(): Promise {
+ await installSkills(this.projectDir)
+ }
+
// Code components in Framer use React 18
private async ensureReact18Types(): Promise {
const reactTypesDir = path.join(this.projectDir, "node_modules/@types/react")
diff --git a/packages/code-link-cli/src/helpers/skills.test.ts b/packages/code-link-cli/src/helpers/skills.test.ts
new file mode 100644
index 000000000..35392e3c6
--- /dev/null
+++ b/packages/code-link-cli/src/helpers/skills.test.ts
@@ -0,0 +1,90 @@
+import fs from "fs/promises"
+import os from "os"
+import path from "path"
+import { describe, expect, it } from "vitest"
+import { findSkillsSourceDir, installSkills } from "./skills.ts"
+
+describe("findSkillsSourceDir", () => {
+ it("locates the skills source directory", async () => {
+ const dir = await findSkillsSourceDir()
+ expect(dir).not.toBeNull()
+
+ const stat = await fs.stat(path.join(dir!, "SKILL.md"))
+ expect(stat.isFile()).toBe(true)
+ })
+})
+
+describe("installSkills", () => {
+ it("copies skill files to .skills//", async () => {
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "cl-skills-"))
+ try {
+ await installSkills(tmpDir)
+
+ // Read skill name from source to know expected dir
+ const sourceDir = await findSkillsSourceDir()
+ const content = await fs.readFile(path.join(sourceDir!, "SKILL.md"), "utf-8")
+ const nameMatch = /^name:\s*(.+)$/m.exec(content)
+ const skillName = nameMatch![1].trim()
+
+ // Verify canonical SKILL.md
+ const skillMd = await fs.readFile(path.join(tmpDir, ".skills", skillName, "SKILL.md"), "utf-8")
+ expect(skillMd).toContain(`name: ${skillName}`)
+
+ // Verify references dir was copied
+ const refsDir = path.join(tmpDir, ".skills", skillName, "references")
+ const refs = await fs.readdir(refsDir)
+ expect(refs.length).toBeGreaterThan(0)
+ expect(refs.some(f => f.endsWith(".md"))).toBe(true)
+ } finally {
+ await fs.rm(tmpDir, { recursive: true, force: true })
+ }
+ })
+
+ it("creates symlinks for agent directories", async () => {
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "cl-skills-"))
+ try {
+ await installSkills(tmpDir)
+
+ const sourceDir = await findSkillsSourceDir()
+ const content = await fs.readFile(path.join(sourceDir!, "SKILL.md"), "utf-8")
+ const nameMatch = /^name:\s*(.+)$/m.exec(content)
+ const skillName = nameMatch![1].trim()
+
+ const agentDirs = [".agents/skills", ".claude/skills", ".cursor/skills"]
+ for (const agentDir of agentDirs) {
+ const linkPath = path.join(tmpDir, agentDir, skillName)
+ const stat = await fs.lstat(linkPath)
+ expect(stat.isSymbolicLink()).toBe(true)
+
+ // Verify the symlink resolves to the canonical SKILL.md
+ const skillMd = await fs.readFile(path.join(linkPath, "SKILL.md"), "utf-8")
+ expect(skillMd).toContain(`name: ${skillName}`)
+ }
+ } finally {
+ await fs.rm(tmpDir, { recursive: true, force: true })
+ }
+ })
+
+ it("is idempotent — skips if already installed", async () => {
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "cl-skills-"))
+ try {
+ await installSkills(tmpDir)
+
+ const sourceDir = await findSkillsSourceDir()
+ const content = await fs.readFile(path.join(sourceDir!, "SKILL.md"), "utf-8")
+ const nameMatch = /^name:\s*(.+)$/m.exec(content)
+ const skillName = nameMatch![1].trim()
+
+ const skillMdPath = path.join(tmpDir, ".skills", skillName, "SKILL.md")
+ const firstStat = await fs.stat(skillMdPath)
+
+ await new Promise(resolve => setTimeout(resolve, 50))
+ await installSkills(tmpDir)
+
+ const secondStat = await fs.stat(skillMdPath)
+ expect(secondStat.mtimeMs).toBe(firstStat.mtimeMs)
+ } finally {
+ await fs.rm(tmpDir, { recursive: true, force: true })
+ }
+ })
+})
diff --git a/packages/code-link-cli/src/helpers/skills.ts b/packages/code-link-cli/src/helpers/skills.ts
new file mode 100644
index 000000000..9c3afc93e
--- /dev/null
+++ b/packages/code-link-cli/src/helpers/skills.ts
@@ -0,0 +1,141 @@
+/**
+ * Agent Skills installer — copies the skill into the project directory
+ * and symlinks it into agent-specific paths.
+ */
+
+import fs from "fs/promises"
+import { fileURLToPath } from "node:url"
+import path from "path"
+
+import { debug } from "../utils/logging.ts"
+
+/** Agent-specific skill directories that get symlinked to the canonical .skills/ location */
+const AGENT_SKILL_DIRS = [
+ ".agents/skills", // Codex, Amp, Gemini CLI, GitHub Copilot, OpenCode, Kimi, Replit
+ ".claude/skills", // Claude Code
+ ".cursor/skills", // Cursor
+]
+
+/**
+ * Read the skill name from the SKILL.md frontmatter.
+ */
+async function readSkillName(sourceDir: string): Promise {
+ const content = await fs.readFile(path.join(sourceDir, "SKILL.md"), "utf-8")
+ const match = /^name:\s*(.+)$/m.exec(content)
+ if (!match) throw new Error("Could not read skill name from SKILL.md frontmatter")
+ return match[1].trim()
+}
+
+/**
+ * Recursively collect all file paths relative to a directory.
+ */
+async function collectFiles(dir: string, base = ""): Promise {
+ const entries = await fs.readdir(dir, { withFileTypes: true })
+ const files: string[] = []
+ for (const entry of entries) {
+ const rel = base ? `${base}/${entry.name}` : entry.name
+ if (entry.isDirectory()) {
+ files.push(...(await collectFiles(path.join(dir, entry.name), rel)))
+ } else {
+ files.push(rel)
+ }
+ }
+ return files
+}
+
+/**
+ * Install the agent skill into the project.
+ * Writes the canonical skill to .skills// and symlinks
+ * into agent-specific directories.
+ */
+export async function installSkills(projectDir: string): Promise {
+ // Find the source skills directory shipped with the package
+ const sourceDir = await findSkillsSourceDir()
+ if (!sourceDir) {
+ debug("Could not locate skills source files, skipping skill installation")
+ return
+ }
+
+ const skillName = await readSkillName(sourceDir)
+ const canonicalDir = path.join(projectDir, ".skills", skillName)
+ const skillMdPath = path.join(canonicalDir, "SKILL.md")
+
+ try {
+ await fs.access(skillMdPath)
+ debug("Agent skills already installed")
+ return
+ } catch {
+ // Not installed yet, proceed
+ }
+
+ // Discover all files in the source skill directory
+ const files = await collectFiles(sourceDir)
+
+ // Copy all skill files to the canonical .skills/ location
+ for (const file of files) {
+ const src = path.join(sourceDir, file)
+ const dest = path.join(canonicalDir, file)
+ try {
+ await fs.mkdir(path.dirname(dest), { recursive: true })
+ await fs.copyFile(src, dest)
+ } catch (err) {
+ debug(`Failed to copy skill file ${file}`, err)
+ return
+ }
+ }
+
+ debug(`Installed agent skill to .skills/${skillName}/`)
+
+ // Create symlinks from each agent directory to the canonical location
+ for (const agentDir of AGENT_SKILL_DIRS) {
+ const linkDir = path.join(projectDir, agentDir)
+ const linkPath = path.join(linkDir, skillName)
+ const relativeTarget = path.relative(linkDir, canonicalDir)
+
+ try {
+ await fs.mkdir(linkDir, { recursive: true })
+
+ // Remove existing symlink/dir if present
+ try {
+ const stat = await fs.lstat(linkPath)
+ if (stat.isSymbolicLink() || stat.isDirectory()) {
+ await fs.rm(linkPath, { recursive: true })
+ }
+ } catch {
+ // Doesn't exist, fine
+ }
+
+ await fs.symlink(relativeTarget, linkPath, "dir")
+ debug(`Symlinked ${agentDir}/${skillName} -> ${relativeTarget}`)
+ } catch (err) {
+ // Symlink failed (e.g. Windows without dev mode), fall back to copy
+ debug(`Symlink failed for ${agentDir}, falling back to copy`, err)
+ try {
+ await fs.cp(canonicalDir, linkPath, { recursive: true })
+ } catch {
+ debug(`Copy fallback also failed for ${agentDir}`)
+ }
+ }
+ }
+}
+
+/**
+ * Find the skills source directory shipped with the package.
+ * Walks up from the current file to find the package root, then resolves skills/.
+ */
+export async function findSkillsSourceDir(): Promise {
+ let dir = path.dirname(fileURLToPath(import.meta.url))
+ for (let i = 0; i < 10; i++) {
+ try {
+ await fs.access(path.join(dir, "package.json"))
+ const candidate = path.join(dir, "skills")
+ await fs.access(path.join(candidate, "SKILL.md"))
+ return candidate
+ } catch {
+ const parent = path.dirname(dir)
+ if (parent === dir) break
+ dir = parent
+ }
+ }
+ return null
+}
diff --git a/packages/code-link-cli/src/index.ts b/packages/code-link-cli/src/index.ts
index 6780e0746..5c85f0c45 100644
--- a/packages/code-link-cli/src/index.ts
+++ b/packages/code-link-cli/src/index.ts
@@ -57,7 +57,7 @@ program
if (detected) {
projectHash = detected
} else {
- console.error("No project ID provided and no existing code-link directory found.")
+ console.error("No Project ID provided and no existing Code Link directory found.")
console.error("Copy the command from the Code Link Plugin to get started.")
process.exit(1)
}
diff --git a/packages/code-link-cli/src/utils/project.ts b/packages/code-link-cli/src/utils/project.ts
index 24e5196c0..f343c08a3 100644
--- a/packages/code-link-cli/src/utils/project.ts
+++ b/packages/code-link-cli/src/utils/project.ts
@@ -4,7 +4,6 @@ import path from "path"
interface PackageJson {
shortProjectHash?: string // derived short id (8 chars base58)
- framerProjectHash?: string // full 64-char hex hash from Framer API
framerProjectName?: string
name?: string
version?: string
@@ -31,7 +30,6 @@ export async function getProjectHashFromCwd(): Promise {
const packageJsonPath = path.join(process.cwd(), "package.json")
const content = await fs.readFile(packageJsonPath, "utf-8")
const pkg = JSON.parse(content) as PackageJson
- // Return short id for port derivation
return pkg.shortProjectHash ?? null
} catch {
return null
@@ -70,7 +68,6 @@ export async function findOrCreateProjectDir(
version: "1.0.0",
private: true,
shortProjectHash: shortId,
- framerProjectHash: projectHash,
framerProjectName: projectName,
}
await fs.writeFile(path.join(projectDir, "package.json"), JSON.stringify(pkg, null, 2))
diff --git a/plugins/code-link/public/icon.svg b/plugins/code-link/public/icon.svg
index 7d90705ce..0bf0ed84c 100644
--- a/plugins/code-link/public/icon.svg
+++ b/plugins/code-link/public/icon.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/plugins/code-link/src/App.tsx b/plugins/code-link/src/App.tsx
index 1999bf7c2..23f1dacee 100644
--- a/plugins/code-link/src/App.tsx
+++ b/plugins/code-link/src/App.tsx
@@ -416,14 +416,18 @@ function InfoPanel({ command }: InfoPanelProps) {
-
Code Link
+
Connect to Terminal
Run the command locally in your terminal to get started.{" "}