From 7920f22e3ca9189f6f0c4f3979b538e7d61c70af Mon Sep 17 00:00:00 2001 From: Raul Lugo Date: Mon, 24 Aug 2026 14:41:42 +0200 Subject: [PATCH] first commit --- .gitignore | 5 + .opencode/.gitignore | 4 + .opencode/skills/likec4/SKILL.md | 25 + .../skills/likec4/references/llms-full.txt | 9154 +++++++++++++++++ .opencode/skills/likec4/references/llms.txt | 43 + AGENTS.md | 11 + README.md | 65 + docs/modeling-conventions.md | 48 + likec4.config.json | 10 + opencode.json | 4 + package-lock.json | 1943 ++++ package.json | 23 + src/model.c4 | 8 + src/specification.c4 | 23 + src/views.c4 | 8 + 15 files changed, 11374 insertions(+) create mode 100644 .gitignore create mode 100644 .opencode/.gitignore create mode 100644 .opencode/skills/likec4/SKILL.md create mode 100644 .opencode/skills/likec4/references/llms-full.txt create mode 100644 .opencode/skills/likec4/references/llms.txt create mode 100644 AGENTS.md create mode 100644 README.md create mode 100644 docs/modeling-conventions.md create mode 100644 likec4.config.json create mode 100644 opencode.json create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/model.c4 create mode 100644 src/specification.c4 create mode 100644 src/views.c4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..afc48d5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +dist/ +generated/ +.DS_Store +*.log diff --git a/.opencode/.gitignore b/.opencode/.gitignore new file mode 100644 index 0000000..1829ecc --- /dev/null +++ b/.opencode/.gitignore @@ -0,0 +1,4 @@ +/node_modules/ +/package.json +/package-lock.json +/bun.lock* diff --git a/.opencode/skills/likec4/SKILL.md b/.opencode/skills/likec4/SKILL.md new file mode 100644 index 0000000..12e351c --- /dev/null +++ b/.opencode/skills/likec4/SKILL.md @@ -0,0 +1,25 @@ +--- +name: likec4 +description: Use when reading, creating, reviewing, or modifying LikeC4 *.c4 files. Enforces LikeC4 nomenclature, syntax, modeling conventions, and recommended practices. +--- + +# LikeC4 modeling + +Before changing any `*.c4` file: + +1. Read `references/llms.txt` to locate the relevant documentation. +2. Consult the applicable sections of `references/llms-full.txt`. +3. Inspect the existing project models and follow `docs/modeling-conventions.md`. +4. Use documented LikeC4 syntax rather than guessing. +5. Follow LikeC4-recommended modeling practices. +6. After editing, run `npm run validate` and resolve any errors caused by the change. + +Pay particular attention to: + +- element and relationship syntax +- specification and model boundaries +- views and predicates +- deployment models +- identifiers, titles, and naming conventions +- supported styling and metadata +- deprecated or discouraged syntax diff --git a/.opencode/skills/likec4/references/llms-full.txt b/.opencode/skills/likec4/references/llms-full.txt new file mode 100644 index 0000000..1eee5f2 --- /dev/null +++ b/.opencode/skills/likec4/references/llms-full.txt @@ -0,0 +1,9154 @@ +# Project config + + +To define a project, create a `likec4.config.json` file in the folder. +All files in the folder (and subfolders) will be part of this project: + + +- externals + - amazon.c4 + - ... +- services + - service1.c4 + - service2.c4 + - ... +- specification.c4 +- **likec4.config.json** + + +## Configuration + +The `likec4.config.json` file must have the **name** of the project. + + +```jsonc +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", // required + "title": "Project Title", // optional + "metadata": { // optional + "owner": "platform-team", + "domain": "payments" + } +} +``` + +The name must be unique if you use [multiple projects](/dsl/config/multi-projects). + +Use `metadata` to store arbitrary project-level key-value data that can be consumed by tooling. + +:::note +You can use these names for the config file: +- `.likec4rc` +- `.likec4.config.json` +- `likec4.config.json` + +LikeC4 interprets any file as JSON5 + +You can also use these names: +- `likec4.config.js` +- `likec4.config.mjs` +- `likec4.config.ts` +- `likec4.config.mts` + +See [Programmatic config](/dsl/config/programmatic) for more information. +::: + +## Extend styles + +If you want to share styles across multiple projects, you can use `extends` in **JSON configs**. +`extends` can be a string or an array of strings. +Each path is resolved relative to the config file that declares it, and `extends` can be recursive. +LikeC4 merges only the `styles` section (in order); all other fields come from the root config. + +```jsonc +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "extends": [ + "../shared/base-config.json", + "../shared/theme-config.json" + ], + "styles": { + "defaults": { + "relationship": { + "arrow": "vee" + } + } + } +} +``` + +## Include additional directories + +You can include LikeC4 source files from directories outside the project folder by adding an `include` configuration to the config file. +This is useful for sharing specifications, common styles, or other model files across multiple projects. + +```json +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "include": { + "paths": [ + "../shared", + "../common/specs" + ] + } +} +``` + + +- shared + - specs.c4 + - common-styles.c4 +- common + - specs + - base-elements.c4 +- my-project + - **likec4.config.json** + - model.c4 + - ... + + +Paths are relative to the project folder (the folder containing the config file). +LikeC4 recursively scans the included directories for `.c4` files. + +You can also configure the scanning behavior: + +```json +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "include": { + "paths": ["../shared"], + "maxDepth": 5, + "fileThreshold": 50 + } +} +``` + +- **`paths`** - Array of relative directory paths (required) +- **`maxDepth`** - Maximum directory depth to scan (default: 3, range: 1-20) +- **`fileThreshold`** - Warn if more than this many files are loaded (default: 30) + +:::note +Include paths must be relative paths. Absolute paths (like `/usr/share`), drive letters (like `C:\`), and URLs are not allowed. +::: + +For more details on sharing files between projects, see [Multi-projects](/dsl/config/multi-projects#share-specification). + +## Exclude files + +By default, LikeC4 recursively scans in the project folder. +You can exclude files by adding an `exclude` array to the config file. + +```json +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "exclude": [ + "**/node_modules/**" + ] +} +``` + +If no exclude pattern is provided, LikeC4 uses `["**/node_modules/**"]` as default. +The exclude pattern is the same as the one used by [picomatch](https://github.com/micromatch/picomatch). + +## Infer technology from icon + +By default, LikeC4 automatically derives a human-readable `technology` label from bundled icon names (`aws:`, `azure:`, `gcp:`, `tech:`) when technology is not explicitly set on the element or its kind. + +For example, an element with `icon tech:apache-flink` gets technology **"Apache Flink"**, and `icon aws:simple-storage-service` gets **"Simple Storage Service"**. `bootstrap:` icons are excluded. + +To disable this behavior: + +```json +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "inferTechnologyFromIcon": false +} +``` + +## Implicit views + +LikeC4 can automatically generate scoped views for elements that don't have explicitly defined views, enabling drill-down navigation — clicking on an element navigates to its auto-generated scoped view (equivalent to `view of element { include * }`). This feature is disabled by default. + +To enable this behavior: + +```json +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "implicitViews": true +} +``` + +## Landing page + +Configure what is shown when visiting the root URL of a generated static site. + +### Redirect to the index view + +Set `redirect` to `true` to go straight to the index view instead of showing the view grid: + +```json +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "landingPage": { + "redirect": true + } +} +``` + +### Filter views by inclusion + +Show only specific views on the landing page grid, by view ID or tag (`#tagName`): + +```json +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "landingPage": { + "include": ["cloud", "#overview"] + } +} +``` + +### Filter views by exclusion + +Hide specific views from the landing page grid: + +```json +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "landingPage": { + "exclude": ["#internal", "legacy-backend"] + } +} +``` + +## Image Aliases + +When using local images in your LikeC4 model, you can create aliases for the folder your images are in to make them more readable and the files more transportable. + +Use the `likec4.config.json` to add an `imageAliases` field: + +```json +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "imageAliases": { + "@": "./images", + "@root": "../../some-more-images" + } +} +``` + +You can then use the alias in your model: + +```likec4 title="example" +// ./amazon.c4 +model { + serviceA = service { + icon: @/service-a.png + } + + serviceB = service { + icon: @root/service-b.png + } +} + +// ./externals/externals.c4 +model { + serviceC = service { + icon: @/service-c.png + } +} +``` + + +- some-more-images + - service-b.png + - ... +- docs + - project + - images + - service-a.png + - service-c.png + - ... + - externals + - externals.c4 + - likec4.config.json + - amazon.c4 + - ... + + +:::note +In the example above the `externals.c4` is a nested file, but the aliasing works based on the project root. +::: + +### Naming Rules + +When using image aliases, keep the following rules in mind: + +- Aliases must start with `@` and can include letters, numbers, and underscores. +- The alias must be unique within the project. +- The alias can point to a relative or absolute path. + +### Defaults + +When no LikeC4 configuration file is found, or when no `imageAliases` field is found, LikeC4 uses the following defaults: + +```json +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "imageAliases": { + "@": "./images", + } +} +``` + +Simply override the `@` to change the default location. + +```json +{ + "imageAliases": { + "@": "./my-images", + } +} +``` + +## Styles customization + +LikeC4 provides advanced style customization capabilities. + +### Theme overrides + +You can override default theme colors and sizes by adding a `styles.theme` section to the config file. +Each definition can be either a CSS value or a detailed object specifying color for specific parts. + +Example of simple color definition: + +```jsonc +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "styles": { + "theme": { + "colors": { + "primary": "#FF6B6B", + "secondary": "rgba(37,99,235,1)", + } + }, + } +} +``` + +Example of detailed color definition: + +```jsonc +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "styles": { + "theme": { + "colors": { + "muted": { + "elements": { + "fill": "#2563eb", // Background color + "stroke": "#1d4ed8", // Border color + "hiContrast": "#ffffff", // Title text color + "loContrast": "#e2e8f0" // Description text color + }, + "relationships": { + "line": "#1d4ed8", // Line color + "label": "#ffffff", // Label text color + "labelBg": "rgba(37,99,235,0.1)" // Label background color + } + }, + // You can give detailed definitions for a custom color in your specification + "custom-color-from-your-spec": { + // ... + } + } + }, + } +} +``` + +You can override sizes used in LikeC4: + +```jsonc +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "styles": { + "theme": { + "sizes": { + "md": { + "width": 200, + "height": 200 + }, + "lg": { + "width": 300, + "height": 300 + } + } + } + } +} +``` + +### Default styles + +You can override default values for style properties by adding a `styles.defaults` section to the config file. +These values will be applied to all elements and relationships, unless properties are explicitly defined in the specification. + +```jsonc +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "project-name", + "styles": { + "defaults": { + // Defaults for all elements + "border": "dashed", + "opacity": 100, + "size": "md", + "color": "primary", // theme color name + + // Defaults for groups + "group": { + "color": "primary", + "opacity": 10, + "border": "dashed" + }, + + // Defaults for relationships + "relationship": { + "color": "gray", + "line": "dashed", + "arrow": "normal" + } + } + } +} +``` + + + +# Multi-projects + + +Sometimes you may want to split your LikeC4 model into multiple ones, based on domains, teams, or any other criteria. + +You can do this by creating multiple projects in your workspace and linking them together. +You can also use this feature to share your model with other teams or projects. + +Create `likec4.config.json` files in the folders you want to be projects: + + +- cloud + - **likec4.config.json** + - service1.c4 + - service2.c4 + - ... +- externals + - **likec4.config.json** + - amazon.c4 + - ... + + +Projects can be nested. +In this case, files from the nested project are not part of the parent project. + + +- cloud + - likec4.config.json + - service1.c4 + - service2.c4 + - nested + - likec4.config.json + - service3.c4 // this will be part of the 'nested' project, not the 'cloud' project + + + +## Import elements + +You can import elements from other projects by using the `import` keyword. + +```likec4 +import { serviceA } from 'projectA' + +model { + serviceB = service { + -> serviceA.api 'calls serviceA' + } +} +``` + + + +## Share specification + +You can share specification files (or any other LikeC4 sources) across multiple projects using the `include` configuration option. + +### Using include paths + +Add an `include` configuration to your project to include files from directories outside the project folder: + +```json +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "cloud", + "include": { + "paths": ["../shared"] + } +} +``` + + +- shared + - specs.c4 + - common-styles.c4 +- cloud + - likec4.config.json + - services.c4 + - ... +- externals + - likec4.config.json + - amazon.c4 + - ... + + +Both `cloud` and `externals` projects can include the shared specification: + +```json title="cloud/likec4.config.json" +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "cloud", + "include": { + "paths": ["../shared"] + } +} +``` + +```json title="externals/likec4.config.json" +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "externals", + "include": { + "paths": ["../shared"] + } +} +``` + +### Include configuration + +The `include` configuration accepts an object with the following properties: + +```json +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "my-project", + "include": { + "paths": ["../shared", "../common/specs"], + "maxDepth": 5, + "fileThreshold": 50 + } +} +``` + +- **`paths`** - Array of relative directory paths (required) +- **`maxDepth`** - Maximum directory depth to scan (default: 3, range: 1-20) +- **`fileThreshold`** - Warn if more than this many files are loaded (default: 30) + + + + + + + +### Using symlinks (alternative) + +Alternatively, you can use symlinks to share files: + + +- shared + - specs.c4 +- cloud + - specs.c4 // -> ../shared/specs.c4 + - likec4.config.json + - ... +- externals + - specs.c4 // -> ../shared/specs.c4 + - likec4.config.json + - ... + + + + +# TypeScript/JavaScript Config + +import { PackageManagers } from 'starlight-package-managers' + +:::caution +This feature is experimental and not well tested with IDEs. + +If you find any issues, please report them to [GitHub](https://github.com/likec4/likec4/issues). +::: + +You can configure your projects programmatically with TypeScript or JavaScript. +The config filename can be any of the following: + +- `likec4.config.js` +- `likec4.config.mjs` +- `likec4.config.ts` +- `likec4.config.mts` + +Example: + +```ts +// likec4.config.ts +import { defineConfig } from 'likec4/config' + +export default defineConfig({ + name: 'my-project', + title: 'My Project', + exclude: ["**/node_modules/**", "**/.cache/**"], + include: ["../shared", "../common/specs"], + imageAliases: { + "@": "./images", + "@root": "../../some-more-images" + } +}) +``` + +:::tip +You can also use smaller package `@likec4/config`: + +```ts +import { defineConfig } from '@likec4/config' + +export default defineConfig({ + name: 'my-project', + title: 'My Project' +}) +``` + +::: + + +## Custom Generators + +LikeC4 CLI has a [`generate`](/tooling/cli/#generate-mermaid-dot-d2-plantuml) command to generate files from your model. +You can define custom generators in your project config: + +```ts +// likec4.config.ts +import { defineConfig } from 'likec4/config' + +export default defineConfig({ + name: 'my-project', + title: 'My Project', + generators: { + 'hello': async ({ likec4model, ctx }) => { + await ctx.write({ + path: 'hello.txt', // relative to the project root + content: `Project: ${likec4model.project.id}`, + }) + }, + }, +}) +``` + +Now you can run your generator with [CLI](/tooling/cli): + +```bash +likec4 gen hello +``` + +In [multi-project](/dsl/config/multi-projects) workspace use: + +```bash +likec4 gen hello --project my-project +# Other options +likec4 gen hello --project my-project --use-dot +``` + +### Reusable Generators + +There is also helper function `defineGenerators` to define reusable generators: + +```ts title="example" +// shared_generators.ts +import { defineGenerators } from 'likec4/config' + +export default defineGenerators({ + 'hello': async ({ likec4model, ctx }) => { + await ctx.write({ + path: 'hello.txt', // relative to the project root + content: `Project: ${likec4model.project.id}`, + }) + }, +}) + +// likec4.config.ts +import { defineConfig } from 'likec4/config' +import generators from './shared_generators' + +export default defineConfig({ + name: 'my-project', + title: 'My Project', + generators, +}) +``` + +## Styles Customization + +Same as in [JSON-config](/dsl/config/#styles-customization), you can define styles in TypeScript config: + +```ts +// likec4.config.ts +import { defineConfig } from 'likec4/config' + +export default defineConfig({ + name: 'my-project', + title: 'My Project', + styles: { + // Theme section allows you to override default colors and sizes + theme: { + colors: { + // Simple color definition - automatically generates element/relationship colors + primary: "#FF6B6B", + secondary: "#4ECDC4", + // Detailed color definition with specific values + muted: { + elements: { + fill: "#2563eb", // Background color + stroke: "#1d4ed8", // Border color + hiContrast: "#ffffff", // Title text color + loContrast: "#e2e8f0" // Description text color + }, + relationships: { + line: "#1d4ed8", // Line color + label: "#ffffff", // Label text color + labelBg: "rgba(37,99,235,0.1)" // Label background color + } + }, + // Give detailed definitions for a color from your specification + "custom-color-from-your-spec": { + // ... + } + }, + sizes: { // override dimensions + md: { + width: 200, + height: 200 + } + } + }, + // Override default values for style properties, + // These values will be used if such property is not defined + defaults: { + border: "dotted", + opacity: 100, + size: "md", + color: "slate", + group: { + color: "green", + opacity: 10, + border: "solid" + }, + relationship: { + color: "indigo", + line: "solid", + arrow: "diamond" + } + } + } +}) +``` + +### Reusable Styles + +There is also helper functions `defineStyle`, `defineTheme` and `defineThemeColor`. +You can export them from a separate file (or publish as a package, to share with others) and import them in your config. + +```ts title="@your-org/likec4-theme" +import { defineStyle, defineTheme, defineThemeColor } from 'likec4/config' + +const theme1 = defineTheme({ + colors: { + primary: "#FF6B6B", + }, +}) + +const theme2 = defineTheme({ + colors: { + primary: "#4ECDC4", + }, +}) + +export default defineStyle({ + // Even use environment variable to switch between themes + theme: process.env['THEME'] === 'theme2' ? theme2 : theme1, + defaults: { + // ... + } +}) +``` + +And in your config: + +```ts +// likec4.config.ts +import { defineConfig } from 'likec4/config' +import styles from '@your-org/likec4-theme' + +export default defineConfig({ + name: 'my-project', + title: 'My Project', + styles, +}) +``` + +
+ + + + +# Deployment Model + + +Deployment Model represents another layer, _physical model_ with its own structure and elements (deployment nodes). +It references the [logical model](/dsl/model) and inherits its relationships. + +## Specification + +First, following the [same approach](/dsl/specification/#element-kind), deployment node kinds have to defined within the specification: + +```likec4 +specification { + deploymentNode environment + deploymentNode zone + deploymentNode kubernetes { + // Nodes have same styling options + style { + color blue + icon tech:kubernetes + multiple true + } + } + deploymentNode vm { + // Common properties for the kind + notation 'Virtual Machine' + technology 'VMware' + } +} +``` + +You define whatever you need to represent your deployment model and your ubiquitous language. + +## Deployment nodes + +The deployment model is a set of nodes, organized in a hierarchical structure: + +```likec4 +deployment { + environment prod { + zone eu { + zone zone1 { + vm vm1 + vm vm2 + } + // You can also use '=' with the name coming first + zone2 = zone { + vm1 = vm + vm2 = vm + } + } + } +} +``` + +Node names must be unique within its container (parent node); same rules as for element names in the logical model. + +Nodes may have properties just like [logical elements](/dsl/model/#element-properties). + +```likec4 +deployment { + environment prod 'Production' { + #live #sla-customer + technology 'OpenTofu' + summary 'Production environment' + description ''' + ## Detailed description + + With **Markdown** support + + ''' + + link https://likec4.dev + + zone eu { + title 'EU Region' + // ... + } + } +} +``` + +### Extend nodes + +As in the logical model, you can [`extend`](/dsl/extend/#extend-element) deployment nodes: + +```likec4 +// File: 'deployments/prod.c4' +deployment { + environment prod +} + +// File: 'deployments/prod/zone-eu.c4' +deployment { + extend prod { + zone eu + } +} +``` + +Same [rules](/dsl/extend/#extend-element) apply for extending nodes: +- extended node must be referenced by a fully qualified name. +- define [additional properties](/dsl/extend/#additional-properties). + +## Deployed instances + +Operator `instanceOf` _“deploys”_ elements from the logical model to deployment nodes: + +```likec4 +deployment { + environment prod { + zone eu { + zone zone1 { + // 'frontend.ui' is a logical element + // by default, instance has same name, + // i.e. it becomes 'prod.eu.zone1.ui' + instanceOf frontend.ui + // this becomes 'prod.eu.zone1.api' + instanceOf backend.api + } + + zone zone2 { + // or use '=' with the name coming first + ui = instanceOf frontend.ui + + // two instances of same element + api1 = instanceOf backend.api + api2 = instanceOf backend.api + } + + // Deploy to any level, not only leaf nodes + // Assume database shared between zones + db = instanceOf database + } + } +} +``` +Deployed instance inherits properties and styling from the element. +It is possible to override: + +```likec4 +deployment { + environment prod { + zone eu { + db = instanceOf database { + title 'Primary DB' + technology 'PostgreSQL with streaming replication' + icon tech:postgresql + style { + color red + } + } + } + } +} +``` + + +## Deployment relationships + +Deployment model inherits relationships from the logical model. +But also allows to define specific ones: + +```likec4 +deployment { + environment prod { + vm vm1 { + db = instanceOf database 'Primary DB' + } + vm vm2 { + db = instanceOf database 'Standby DB' + } + vm2.db -> vm1.db 'replicates' + } +} +``` +As you see, relationship is between same logical element, but different instances. +Assume, we don't need this relationship in our logical model, but it makes sense for deployment. + +Deployment relationships can be "kinded", and have [same properties](/dsl/relationships/#relationship-properties) as logical ones: + +```likec4 +deployment { + environment prod { + vm2.db -[streaming]-> vm1.db { + #next, #live + title 'replicates' + description 'Streaming replication' + } + } +} +``` + +:::tip +Relationships can be defined for nested elements of deployed instances: + +```likec4 +model { + component database { + component repl_log + } +} +deployment { + vm vm1 { + db = instanceOf database 'Primary DB' + } + vm vm2 { + db = instanceOf database 'Standby DB' + } + + // 'repl_log' is a nested element of deployed instance + vm2.db -> vm1.db.repl_log 'replicates' +} +``` +::: + +:::note +Check this GitHub discussion for further development. +Feel free to share your ideas. +::: + +# Deployment views + + +Deployment views allow you to visualize the deployment model, using same approach as [model views](/dsl/views/predicates) — predicates. + +## View definition + +```likec4 {17-23} +deployment { + environment prod { + zone eu { + zone zone1 { + instanceOf frontend.ui + instanceOf backend.api + } + zone zone2 { + instanceOf frontend.ui + instanceOf backend.api + } + instanceOf database + } + } +} +views { + deployment view index { + title 'Production Deployment' + link https://likec4.dev + + include prod.** + // ... + } +} +``` + +## View predicates + +Deployment views are based on same [predicates](/dsl/views/predicates) as model views. +But they refer to deployment nodes and instances. + + + +### Filtering +Filtering in deployment views use the same principles as in normal views but takes into account deployment nodes, relations, tags and metadata defined in deployment model. +When condition on element is checked the following rules are applied: +- For deployment instances, tags are combined from tags defined in model and tags defined in deployment model +- For a child of deployment instance the tags defined on child in model are used +- For a deployment node the tags defined in deployment model are used +- For deployment instances, the kind of the model element is used +- For a child of deployment instance the kind of this child is used +- For a deployment node the kind of this deployment node is used +- For deployment instances, metadata defined in deployment model replaces metadata from the model element +- For relationship endpoints, `source.metadata.*` and `target.metadata.*` use the same deployed instance metadata rules +- For deployment nodes, metadata defined in deployment model is used +- Tags are not inherited from parent nodes/elements + +```likec4 +model { + element cloud { + element frontend { + #next + -> backend "rel1" + } + element backend { + #next + -> db "rel2" + } + element db + } +} +deployment { + environment prod { // Resulting tags: #alpha + #alpha + zone eu { // Resulting tags: #beta + #beta + instanceOf frontend { // Resulting tags: #next, #gamma + #gamma + } + instanceOf backend { // Resulting tags: #next, #sigma + #sigma + } + eu -> prod.db "rel3" + } + instanceOf db { // Resulting tags: #delta + #delta + } + } +} +views { + deployment view some { + include prod.eu.frontend -> prod.eu.backend + where source.tag is #next // includes relation "rel1" + include prod.eu.frontend -> prod.eu.backend + where source.tag is #gamma // includes relation "rel1" + include prod.eu -> prod.db + where source.tag is #beta // includes relation "rel3" + include prod.eu -> prod.db + where source.tag is #sigma // does not include any relations + include eu.* -> prod.db + where source.tag is #sigma // includes relations "rel2" + } +} +``` + +Metadata predicates can be used with `include` and `exclude` as well: + +```likec4 +deployment { + environment prod { + instanceOf backend { + metadata { + zone 'public' + } + } + instanceOf database { + metadata { + zone 'restricted' + } + } + } +} + +views { + deployment view prod { + include * -> * + exclude * -> * + where target.metadata.zone is "restricted" + } +} +``` + +### Include ancestors + +Sometimes, it's useful to visualize exactly how elements are deployed. In this case, you can use +the `includeAncestors` attribute on a deployment view. + +The purpose of this attribute is to force all ancestors of already visible elements to show in the final diagram. +It will not affect the visible relationships. It's just a representation preference allowing to view how each relevant elements are deployed. + +```likec4 +specification { + element service + + deploymentNode hypervisor { + style { + color green + } + } + deploymentNode vm { + style { + color red + } + } + deploymentNode app_server { + style { + color sky + } + } +} + +model { + svc1 = service + svc2 = service + + svc1 -> svc2 +} + +deployment { + hyp1 = hypervisor { + vm = vm { + tomcat1 = app_server "tomcat" { + instanceOf svc1 + } + } + } + hyp2 = hypervisor { + vm = vm { + tomcat2 = app_server "tomcat" { + instanceOf svc2 + } + } + } +} + +views { + view index { + title 'Landscape' + include * + } + + deployment view ancestors_test { + includeAncestors: true + + include hyp1.tomcat1.svc1 + include hyp2.tomcat2.svc2 + } +} +``` + + +
+
+ + +# Extending model + + +You can extend the model by creating new files and folders. +When LikeC4 source files are parsed, they are _merged_ into a single architecture model. + +You are free to organize the workspace as you want. + + +## Example + +Assume we have the following workspace: + + +- cloud + - service1.c4 + - service2.c4 + - ... +- externals + - amazon.c4 +- landscape.c4 +- specs.c4 + + + + + + This file defines the specification: + + ```likec4 + specification { + element actor { + style { + shape person + } + } + element system + element service + } + ``` + + + This file defines the top-level elements and landscape view: + + ```likec4 + model { + customer = actor 'Customer' + cloud = system 'Cloud System' + } + views { + view index of cloud { + title "Cloud System - Landscape" + include * + } + } + ``` + + + We keep definitions of external systems separately, inside the `externals/` folder: + + ```likec4 + model { + amazon = system 'Amazon Web Services' { + rds = service 'Database' + } + } + ``` + + + +## Extend element + +`extend` is a way to enrich the model and define nested elements in a separate file. + + +We don't want to mess up the _landscape.c4_ file with the internals of the `cloud`. +In a separate file we extend `cloud` and define `cloud.service1`: + +```likec4 +// cloud/service1.c4 +model { + // cloud is defined in landscape.c4 + extend cloud { + // extend and define cloud.service1 + service1 = service 'Service 1' + } +} +``` + +The element extension inherits the scope of the target (or, more accurately, the _**parent**_). +For example: + +```likec4 +// cloud/service2.c4 +model { + // cloud is defined in landscape.c4 + extend cloud { + // extend and define cloud.service2 + service2 = service 'Some Service 2' + + service2 -> service1 // ✅ service1 is known inside 'cloud' + } +} +``` + + + +### Additional properties + +You can extend element with additional tags, links and metadata: + +```likec4 +model { + extend cloud { + // Add tags + #additional-tag, #another-tag + + // Add metadata + metadata { + prop1 'value1' + } + + // Add links + link ../src/index.ts#L1-L10 + } +} +``` + +#### Metadata Merging + +When extending elements with metadata, duplicate keys from both the original element and the extension are merged into arrays: + +```likec4 +model { + component api { + metadata { + version '1.0.0' + tags 'backend' + regions 'us-east-1' + } + } +} + +// In another file +model { + extend api { + metadata { + tags 'microservice' // Merges with existing 'backend' + regions ['eu-west-1'] // Merges with existing 'us-east-1' + owner 'platform-team' // New key + } + } +} + +// Result: +{ + version: '1.0.0', + tags: ['backend', 'microservice'], // Merged and kept as array + regions: ['us-east-1', 'eu-west-1'], // Merged and kept as array + owner: 'platform-team' +} +``` + +**Merging behavior:** +- Duplicate values are automatically de-duplicated +- If after de-duplication there's only one unique value, it's stored as a string (not an array) +- Arrays from both sides are merged and de-duplicated + +```likec4 +model { + component api { + metadata { + environment 'production' + tags ['backend', 'api'] + } + } +} + +model { + extend api { + metadata { + environment 'production' // Duplicate value + tags ['api', 'critical'] // 'api' is duplicate + } + } +} + +// Result: +{ + environment: 'production', // Single value (de-duplicated) + tags: ['backend', 'api', 'critical'] // Merged and de-duplicated +} +``` + +You can extend the same element multiple times across different files, and all metadata will be properly merged: + +```likec4 +// file1.c4 +model { + component api { + metadata { + version '2.0.0' + tags 'backend' + } + } +} + +// file2.c4 +model { + extend api { + metadata { + tags 'rest' + owner 'team-a' + } + } +} + +// file3.c4 +model { + extend api { + metadata { + tags 'microservice' + regions ['us', 'eu'] + } + } +} + +// Result: +{ + version: '2.0.0', + tags: ['backend', 'rest', 'microservice'], + owner: 'team-a', + regions: ['us', 'eu'] +} +``` + +## Extend Relationship + +Similar to extending elements, you can extend relationships to add metadata, tags, and links from separate files. + + + +Relations are uniquely identified by: +- Source element +- Target element +- Relationship kind (if specified) +- Title (if specified) + +```likec4 +// base.c4 +specification { + element component + relationship sync +} + +model { + component frontend + component api + + // Untyped relation with title + frontend -> api "Makes requests" + + // Typed relation with same title (different relation!) + frontend -[sync]-> api "Makes requests" +} + +// extend-ops.c4 +model { + // Extend the untyped relation + extend frontend -> api "Makes requests" { + metadata { + latency_p95 '150ms' + rate_limit '1000req/s' + } + } + + // Extend the sync relation (different from above!) + extend frontend -[sync]-> api "Makes requests" { + metadata { + latency_p95 '80ms' + cache_enabled 'true' + } + } +} +``` + + + +### Metadata Merging + +Relationship extends follow the same metadata merging rules as element extends: +- New keys are added +- Duplicate keys with different values become arrays +- Duplicate keys with the same value are de-duplicated + +```likec4 +// base.c4 +model { + api -> database "Queries data" { + metadata { + protocol 'TCP' + } + } +} + +// extend-1.c4 +model { + extend api -> database "Queries data" { + metadata { + protocol 'SSL' // Creates array + timeout '5s' // New key + } + } +} + +// extend-2.c4 +model { + extend api -> database "Queries data" { + metadata { + protocol 'TLS' // Adds to array + retry_policy 'exponential' // New key + } + } +} + +// Result: +{ + protocol: ['TCP', 'SSL', 'TLS'], + timeout: '5s', + retry_policy: 'exponential' +} +``` + +# Introduction + + +LikeC4 is a DSL for describing software architecture. + +Source files must have `.likec4` or `.c4` extensions. +All sources are merged into _a single model_ (explained later in [extending the model](/dsl/extend)). + +A project may look like this: + + +- backend + - service1 + - model.c4 + - views.c4 + - service2 + - model.c4 + - ... +- externals + - amazon.c4 + - ... +- landscape.c4 +- specs.c4 + + +## Top-level statements + +A source file should have at least one of these statements: + +- `specification` - defines element kinds to be used in the model, like **system**, **app**, **microservice**... +- `model` - architecture elements, hierarchies, compositions and relationships +- `views` - visualizations +- `global` - globally shared predicates (explained later in [Views](/dsl/views/predicates/#shared-global-styles)) + +```likec4 +// example.c4 +specification { + //... +} + +global { + //... +} + +model { + //... +} + +views { + //... +} +``` + +You can have multiple statements of the same type: + +```likec4 +// Views group 1 +views { +} + +// Views group 2 +views { +} +``` + +:::tip +For example, the `views` block allows _"local styles"_ that apply to views in the same block. +This way you can group views that have same styles, and avoid boilerplate. +Explained later in [Views](/dsl/views/predicates/#shared-local-styles). +::: + +# Model + + +The `model` describes the architecture as a set of hierarchical elements and relationships among them. + +## Element + +An element is a basic building block. It represents a logical part of the architecture. +Any element must have a [`kind`](/dsl/specification#element-kind) and a `name` (_identifier_): + +```likec4 +specification { + element actor + element service +} + +model { + // element of kind 'actor' with the name 'customer' + actor customer + // element of kind 'service' named as 'cloud' + service cloud + + // also possible with '=' and the name goes first + cloud = service +} +``` + +An element name is required for references. +It can contain letters, digits, hyphens, and underscores, but cannot start with a digit or contain `.` + +| name | valid | +| :--------- | :-- | +| api | ✅ | +| Api2 | ✅ | +| \_api | ✅ | +| \__Api-1 | ✅ | +| 1api | ⛔️ | +| a.pi | ⛔️ | + + +## Element Properties + +### Title + +```likec4 'SaaS' copy +specification { + element softwareSystem +} +model { + // Title can be inlined + saas = softwareSystem 'SaaS' + + // or nested + saas = softwareSystem { + title 'SaaS' + + // You can use `:` (optional) + title: 'SaaS' + } + + // If title is not specified, name will be used by default + saas = softwareSystem +} +``` + +:::note +You can use single or double quotes: +```likec4 +model { + service cloud 'Cloud Service' + // Or + service cloud "Cloud Service" +} +``` +If you need quotes inside, escape with backslash: +```likec4 +model { + service cloud 'Cloud\'s Service' + service cloud "Cloud\"s Service" +} +``` +::: + +### Description + +```likec4 'Provides services to customers' copy +model { + // Can be inlined + saas = softwareSystem 'SaaS' 'Provides services to customers' + + // or nested + saas = softwareSystem { + title 'SaaS' + description 'Provides services to customers' + } +} +``` + +An element may have a short `summary` (optional; falls back to `description`): + +```likec4 copy +model { + saas = softwareSystem { + title 'SaaS' + summary 'Provides services to customers' + description ' + Detailed description + ... + ' + } +} +``` + +If `summary` is provided, it will be shown on the diagram, and the `description` will be shown in the details dialog. +If you don't provide `description`, summary will be used. + +For inlined definition: +```likec4 copy +model { + // [title] [summary] + saas = softwareSystem 'SaaS' 'Provides services to customers' { + description ' + Detailed description + ... + ' + } +} +``` + +### Technology + +```likec4 copy +model { + api = service { + technology 'REST' + } + + // Structurizr DSL style: + // = softwareSystem [title] [summary] [technology] + saas = softwareSystem 'SaaS' 'Provides services to customers' 'SaaS' +} +``` + +:::tip +You can define element properties in the [specification](/dsl/specification#element-kind) if they are common to all elements of that kind: + +```likec4 +specification { + element mobileApp { + title 'Mobile App' + description 'Universal mobile application' + technology 'React Native' + } +} +``` + +This allows you to define properties once and reuse them across the model. +Properties from the element definition override the ones from the specification. +::: + +:::note[Auto-derived technology] +If an element has a bundled icon (`aws:`, `azure:`, `gcp:`, or `tech:`) but no explicit technology, +LikeC4 automatically derives a human-readable technology label from the icon name. +For example, `icon tech:apache-flink` produces technology **"Apache Flink"**, and `icon aws:simple-storage-service` produces **"Simple Storage Service"**. + +`bootstrap:` icons are excluded because they are generic UI icons. + +This behavior is enabled by default. To disable it, set `inferTechnologyFromIcon` to `false` in the [project config](/dsl/config). +::: + +### Tags + +Element [tags](/dsl/specification/#tag) are defined in a nested block and must come first, before any other properties: + +```likec4 copy +model { + appV1 = application 'App v1' { + #deprecated + description 'Old version of the application' + } + + // multiple tags + appV2 = application { + #next, #serverless + #team2 + title 'App v2' + } + + appV3 = application { + title 'App v3' + #team3 // ⛔️ Error: tags must be defined first + } +} +``` + +:::tip + +You can add tags in the [specification](/dsl/specification/#tag), if it is common for all of the kind: + +```likec4 +specification { + element lambda { + #serverless + } +} +``` +::: + +### Links + +An element may have multiple links: + +```likec4 copy +model { + bastion = application 'Bastion' { + // External link + link https://any-external-link.com + + // With label + link https://github.com/likec4/likec4 'Repository' + + // or any URI + link ssh://bastion.internal 'SSH' + + // or relative link to navigate to sources + link ../src/index.ts#L1-L10 + } +} +``` + +### Metadata + +Element metadata is a set of key-value pairs defined in a nested block: + +```likec4 copy +model { + app = application 'App' { + metadata { + prop1 'value1' + prop2 ' + apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: app-statefulset + spec: {} + ' + prop3 '{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "age": { + "type": "integer" + } + } + }' + } + } +} +``` + +Only string values are allowed, but you can use JSON or YAML format for complex data. + +### Array Values + +You can also use arrays for metadata values using array literal syntax: + +```likec4 copy +model { + app = application 'App' { + metadata { + tags ['frontend', 'react', 'typescript'] + environments ['dev', 'staging', 'prod'] + version '2.1.0' + } + } +} +``` + +Mixed single and array values are supported in the same metadata block: + +```likec4 copy +model { + api = service 'API Gateway' { + metadata { + version '3.2.1' + maintainer 'Platform Team' + tags ['backend', 'gateway', 'microservice'] + regions ['us-east-1', 'eu-west-1'] + critical true + } + } +} +``` + +Here are more examples showing various mixed metadata patterns: + +```likec4 copy +model { + // E-commerce application with mixed metadata types + frontend = application 'Frontend App' { + metadata { + framework 'React' + version '18.2.0' + features ['shopping-cart', 'user-auth', 'payment', 'search'] + deployment_targets ['staging', 'production'] + team_lead 'Alice Johnson' + developers ['Bob Smith', 'Carol Davis', 'David Wilson'] + release_cycle 'weekly' + supported_browsers ['Chrome', 'Firefox', 'Safari', 'Edge'] + accessibility_level 'WCAG 2.1 AA' + has_mobile_app true + } + } + + // Database service with operational metadata + database = service 'PostgreSQL Cluster' { + metadata { + engine 'PostgreSQL' + version '15.3' + instances ['primary', 'replica-1', 'replica-2'] + backup_schedule 'daily' + backup_retention_days '30' + monitoring_endpoints ['metrics', 'logs', 'traces'] + alert_channels ['slack', 'email', 'pagerduty'] + maintenance_window 'Sunday 2-4 AM UTC' + data_classification 'sensitive' + encryption_at_rest true + } + } + + // Microservice with complex deployment metadata + payment = service 'Payment Service' { + metadata { + language 'Go' + version '2.1.4' + port '8080' + health_check_path '/health' + dependencies ['database', 'redis', 'external-payment-api'] + environments ['dev', 'test', 'stage', 'prod'] + scaling_policy 'auto' + min_replicas '2' + max_replicas '10' + circuit_breaker_enabled true + rate_limits ['1000/minute', '100/second'] + compliance_standards ['PCI-DSS', 'SOC2'] + } + } +} +``` + +### Metadata Properties Behavior + +**Alphabetic Ordering**: Metadata properties are automatically sorted alphabetically when displayed, regardless of the order in which they are defined in the DSL. This ensures consistent presentation across all elements. + +**Property Duplication**: When the same property name is defined multiple times, all values are collected into an array, preserving their definition order: + +```likec4 copy +model { + service = component 'Payment Service' { + metadata { + version '1.0.0' // First value + version '2.0.0' // Second value + // Result: version: ['1.0.0', '2.0.0'] + + owner ['team-a', 'team-b'] // First: array values + owner 'team-c' // Second: single value + // Result: owner: ['team-a', 'team-b', 'team-c'] + + tags 'primary' // First: single value + tags ['backend', 'critical'] // Second: array values + // Result: tags: ['primary', 'backend', 'critical'] + + ports ['8080', '9090'] // First: array values + ports ['3000', '4000'] // Second: array values + // Result: ports: ['8080', '9090', '3000', '4000'] + } + } +} +``` + +This behavior applies to **all duplicate keys**. + +All metadata properties are displayed alphabetically, regardless of definition order. + +```likec4 copy +model { + api = service 'API Gateway' { + metadata { + version '3.2.1' + maintainer 'Platform Team' + tags ['backend', 'gateway', 'microservice'] + regions ['us-east-1', 'eu-west-1'] + critical true + } + } +} +``` + +Here are more examples showing various mixed metadata patterns: + +```likec4 copy +model { + // E-commerce application with mixed metadata types + frontend = application 'Frontend App' { + metadata { + framework 'React' + version '18.2.0' + features ['shopping-cart', 'user-auth', 'payment', 'search'] + deployment_targets ['staging', 'production'] + team_lead 'Alice Johnson' + developers ['Bob Smith', 'Carol Davis', 'David Wilson'] + release_cycle 'weekly' + supported_browsers ['Chrome', 'Firefox', 'Safari', 'Edge'] + accessibility_level 'WCAG 2.1 AA' + has_mobile_app true + } + } + + // Database service with operational metadata + database = service 'PostgreSQL Cluster' { + metadata { + engine 'PostgreSQL' + version '15.3' + instances ['primary', 'replica-1', 'replica-2'] + backup_schedule 'daily' + backup_retention_days '30' + monitoring_endpoints ['metrics', 'logs', 'traces'] + alert_channels ['slack', 'email', 'pagerduty'] + maintenance_window 'Sunday 2-4 AM UTC' + data_classification 'sensitive' + encryption_at_rest true + } + } + + // Microservice with complex deployment metadata + payment = service 'Payment Service' { + metadata { + language 'Go' + version '2.1.4' + port '8080' + health_check_path '/health' + dependencies ['database', 'redis', 'external-payment-api'] + environments ['dev', 'test', 'stage', 'prod'] + scaling_policy 'auto' + min_replicas '2' + max_replicas '10' + circuit_breaker_enabled true + rate_limits ['1000/minute', '100/second'] + compliance_standards ['PCI-DSS', 'SOC2'] + } + } +} +``` + +```likec4 copy +model { + api = service 'API Gateway' { + metadata { + version '3.2.1' + maintainer 'Platform Team' + tags ['backend', 'gateway', 'microservice'] + regions ['us-east-1', 'eu-west-1'] + critical true + } + } +} +``` + +```likec4 copy +model { + api = service 'API Gateway' { + metadata { + version '3.2.1' + maintainer 'Platform Team' + tags ['backend', 'gateway', 'microservice'] + regions ['us-east-1', 'eu-west-1'] + critical true + } + } +} +``` + +## Using Markdown + +You can use markdown in `description` (and `summary`) with triple quotes: + +```likec4 copy +model { + mobile = application { + title 'Mobile Application' + description ''' + ### Multi-platform application + + [React Native](https://reactnative.dev) + ''' + } + + web = application { + description """ + ### Web Application + + > Provides services to customers through + > the web interface. + + | checks | | + | :--------- | :-- | + | check 1 | ✅ | + | check 2 | ⛔️ | + | check 3 | ✅ | + """ + } +} +``` + +## Structuring Model + +Any element can act as a container and include other elements. +This way you define the structure and internals of the element. + +```likec4 filename="nested-elements.c4" +model { + // service1 has backend and frontend + service service1 { + component backend { + // backend has api + component api + } + component frontend + } + + // or use '=' + service2 = service { + backend = component { + api = component + } + frontend = component + } +} +``` + +Nested elements are _"namespaced"_: the parent name is used as a prefix. +So, the model above has the elements with these fully qualified names: + +- `service1` +- `service1.backend` +- `service1.backend.api` +- `service1.frontend` + +and: + +- `service2` +- `service2.backend` +- `service2.backend.api` +- `service2.frontend` + +:::caution +You cannot have elements with the same name within the same parent. +It is explained in detail in [references](/dsl/references). + +```likec4 filename="nested-elements.c4" +model { + + service service1 'Service 1' { + component backend + + component backend // ⛔️ Error: 'service1.backend' already defined + } + + service service2 'Service 2' { + component backend // ✅ This is OK - 'service2.backend' + + component legacy { + component backend // ✅ This is OK - 'service2.legacy.backend' + } + } + + component backend // ✅ This is OK - 'backend' +} +``` +::: + +# View Notations + + + + + + +View notations (or key/legend) provide information about the meanings of shapes and styles. +It's important to provide a key explaining the difference in colors and shapes. + + +It is possible to define global notations (per element kind) and local (per view). + +#### Global notations + +As comes from the name, global notations apply to all views, and defined in the `specification` block: + +```likec4 copy {4, 12} +specification { + + element customer { + notation "Person, Customer" + style { + shape person + color green + } + } + + element staff { + notation "Person, Staff" + style { + shape person + } + } +} +``` + +Notations will be applied to all views with these elements and displayed as: + +
+![notations](../../../assets/notations.png) +
+ +Live example. +Expand the following view and click on help icon in bottom right: + + + +#### Local notations + +Local notations override global ones and apply to a specific view. + +##### With style predicate + +When you change the style, you can add a notation to explain the meaning: + +```likec4 +view { + + style webApp1, webApp2 { + notation "Application under development" + color amber + } + + style element.tag = #deprecated { + notation "Deprecated" + color muted + } + +} +``` + +Notations are not merged or grouped, the last one will be applied. + +:::tip + +You may have same notation for different styles: + +```likec4 +view { + + style webApp1 { + notation "Web Application" + color amber + } + + style webApp2 { + notation "Web Application" + color green + } +} +``` +::: + + +##### With overrides + +Define notation when include elements: + +```likec4 +view { + + include * + where kind is microservice + and tag is #deprecated + with { + notation "Deprecated microservice" + shape rectangle + color muted + } + +} +``` + +This override has the highest priority, then defined within style predicates, and finally global notations. + +:::note +Feel free to share your ideas in GitHub discussions how to improve notations, make reusable or more flexible. +::: + +# References + + +LikeC4 uses the lexical scope with hoisting, almost like in JavaScript. + +## Scope + +To understand references, we need to understand scopes first. +Example: + +```likec4 +model { + service service1 { + component api + component frontend + } +} +``` + +Every element is unique in the model, so we can add a relationship referencing them, like: + +```likec4 'frontend -> api' +model { + service service1 { + component api + component frontend + } + frontend -> api +} +``` + +But if we add `service2` with another `api`: + +```diff lang="likec4" +model { + service service1 { + component api + component frontend + } ++ service service2 { ++ component api ++ } + + frontend -> api // ⛔️ Error: 'api' not found +} +``` + +The reference is ambiguous, as there are two `api` components in the model. + +Every element creates a new scope inside `{...}`, so `api` is unique inside `service1` and `service2`, +but not in the scope of `model`. + +We can resolve by moving the relationship to the scope of `service2`: + +```likec4 {9-11} +model { + service service1 { + component api + component frontend + } + service service2 { + component api + + frontend -> api // ✅ This is OK, + // 'api' is unique in 'service2' + // 'frontend' is unique in 'model' + } +} +``` + +## Hoisting + + + +In LikeC4, the element, besides being hoisted in its scope, also _"bubbles"_ to the upper scopes, if it stays unique. + +We may reference something that is not yet declared but will be hoisted later. +The relationship on line 8 references `graphql` defined below on line 15: + +```likec4 showLineNumbers copy +model { + + service service1 { + component api + component frontend + + frontend -> api // ✅ This is OK, references to 'api' from 'service1' + frontend -> graphql // ✅ This is OK, references to unique 'graphql' + } + + frontend -> api // ⛔️ Error: 'api' is ambiguous + + service service2 { + component api + component graphql + + frontend -> api // ✅ This is OK, references to 'api' from 'service2' + } + +} +``` + + + +## Fully qualified names + +Top-level elements (placed directly in the `model` block) are available globally. +You can use fully qualified names (FQN) to reference nested elements. + +Example: + +```likec4 {11,12} +model { + service service1 { + component api + component frontend + } + service service2 { + component api + } + + frontend -> api // ⛔️ Error: 'api' not found + frontend -> service1.api // ✅ This is OK + frontend -> service2.api // ✅ This is OK +} +``` + +Or even: + +```likec4 {6} +model { + service service1 { + component api + component frontend { + -> api + -> service2.api // references to outer scope + } + } + service service2 { + component api + } +} +``` + +Some parts may be omitted, if FQN stays unique: + +```likec4 +model { + service service { + component backend1 { + component api + } + component backend2 { + component api + component graphql + } + } + + frontend -> service.backend1.api // ✅ Non-ambiguous fully qualified name + + frontend -> backend1.api // ✅ This is OK, 'api' is unique in 'backend1', + // and 'backend1' is unique in the model + // We may omit 'service' + + frontend -> backend2.api // ✅ This is also OK + + frontend -> service.api // ⛔️ Error: 'api' is ambiguous in 'service' + + frontend -> service.graphql // ✅ This is also OK, we omit 'backend2' + // as 'graphql' is unique in 'service' +} +``` + + + +# Relationships + + +Relationships describe the connections, data flows and interactions within your model. + +## Relationship definition + +Relationships are defined with the **`->`** operator: + +```likec4 +model { + customer = actor 'Customer' + cloud = service 'Cloud' + + customer -> cloud +} +``` + +Relationships may be nested + +```likec4 +model { + service cloud { + component backend + component frontend + + frontend -> backend + customer -> frontend + } +} +``` + +In nested relationships you can use `it` or `this` to refer parent: + +```likec4 +model { + customer = actor { + // as a source + it -> frontend + // as a target + frontend -> this + } +} +``` + +Nested relationships may be _"sourceless"_, then the source is the parent element + +```likec4 +model { + actor customer { + // same as customer -> frontend + -> frontend + } + service cloud { + component backend + component frontend { + // same as frontend -> backend + -> backend + } + } +} +``` + +:::caution + +_"sourceless"_ relationships must be nested: + +```likec4 +model { + -> backend // ⛔️ Error: model can't be a source +} +``` +::: + +## Relationship kinds + +Relationships can be "kinded": + +```likec4 +specification { + element system + // Define relationship kind + relationship async + relationship uses +} + +model { + system1 = system 'System 1' + system2 = system 'System 2' + + system1 -[async]-> system2 + + // Or prefix with '.' to use the kind + system1 .uses system2 +} +``` + +This makes it possible to add richer semantics to the interactions between elements, for example, +from a technology perspective (REST, gRPC, GraphQL, Sync/Async, etc.) +or from a business perspective (delegation, informing, accountability, etc.). + +You can define whichever relationship types best fit your context. + +A relationship kind can also define [tags](#tags) and default properties — `title`, `description`, `technology`, `notation` and [links](#links) — inherited by every relationship of that kind (a relationship may override any of the properties; tags are merged): + +```likec4 +specification { + relationship async { + #tcp + title 'Asynchronous' + technology 'Kafka' + } + tag tcp +} +model { + // inherits tag #tcp, title 'Asynchronous' and technology 'Kafka' + system1 .async system2 + + // overrides the title, keeps the inherited tag and technology + system1 .async system3 'publishes events' +} +``` + + + +## Relationship Properties + +### Title + +Relationships may have a title (and it's better to have one): + +```likec4 'opens in browser' +model { + customer -> frontend 'opens in browser' + // or nested + customer -> frontend { + title 'opens in browser' + } +} +``` + +### Description + +```likec4 'Customer opens...' +model { + customer -> frontend 'opens in browser' { + description 'Customer opens...' + } + + // Or in a shorter way + customer -> frontend 'opens in browser' 'Customer opens...' +} +``` + +As with elements, you can [use markdown](/dsl/model#using-markdown) in the `description` with triple quotes: + +```likec4 +model { + customer -> frontend 'opens in browser' { + description ''' + **Customer** opens the frontend in the browser + to interact with the system + + + | checks | | + |:--------- |:-- | + | check 1 | ✅ | + | check 2 | ⛔️ | + | check 3 | ✅ | + ''' + } +} +``` + +### Technology + +```likec4 +model { + customer -> frontend 'opens in browser' { + technology 'HTTPS' + } + + // Or in a shorter way + // order is [title] [description] [technology] + customer -> frontend 'opens in browser' 'Customer opens...' 'HTTPS' +} +``` + + +### Tags + +Relationships can have tags: + +```likec4 +model { + // inlined + frontend -> backend 'requests data' #graphql #team1 + + // or nested + customer -> frontend 'opens in browser' { + #graphql #team1 + } +} +``` + +### Links + +Relationships can have multiple links: + +```likec4 copy +model { + customer -> frontend 'opens in browser' { + // External link + link https://any-external-link.com + + // With label + link https://github.com/likec4/likec4 'Repository' + + // or any URI + link ssh://bastion.internal 'SSH' + + // or relative link to navigate to sources + link ../src/index.ts#L1-L10 + } +} +``` + + +### Navigate To + +A relationship may have a `navigateTo` property, which links to a [dynamic view](/dsl/views/dynamic). +This allows to _"zoom-in"_ and see more details about this relationship. + +```likec4 copy +model { + webApp -> backend.api { + title 'requests data for the dashboard' + navigateTo dashboard-request-flow + } + +} +``` + +## Relationships Metadata + +Same as [element metadata](/dsl/model/#metadata): + +```likec4 +model { + customer -> frontend 'opens in browser' { + metadata { + prop1 'value1' + prop2 '{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "age": { + "type": "integer" + } + } + }' + } + } +} +``` + +# Specification + +In the `specification`, you define your notation. + +## Element kind + +Defines element kinds that are used in the model: + +```likec4 copy +specification { + // Define whatever you want + element user + element cloud + element system + element application + element component + element controller + element microservice + element queue + element restapi + element graphqlMutation + element repository + element database + element pgTable +} +``` + +You'll learn later that element kinds may have properties and define styles: + +```likec4 +specification { + element queue { + title 'Kafka' + description 'Kafka queue' + technology 'kafka topic' + notation 'Kafka Topic' + style { + shape queue + } + } +} +``` + +## Relationship + +Relationship kinds may define tags, properties (`title`, `description`, `technology`, `notation`, links) and visual styles: + +```likec4 +specification { + relationship async { + #tcp + title 'Asynchronous' + description 'Communication over a message broker' + technology 'Kafka' + notation 'Async' + link https://example.com/async + multiple true + line dotted + color amber + } + relationship subscribes + relationship is-downstream-of + + tag tcp +} +``` + +These properties are inherited by every relationship of that kind, unless the relationship overrides them (tags are merged): + +```likec4 +model { + // inherits the title and style defined for the 'async' kind + ui .async backend + + // overrides the title, keeps the inherited style + ui .async db 'reads from' +} +``` + +More in the [relationships](/dsl/relationships/#relationship-kinds) + +## Tag + +Tags can be used to mark, group, or filter elements/relationships/views, or to add additional semantics, like `#deprecated`, `#epic-123`, or `#team2`. + +```likec4 +specification { + tag deprecated + tag epic-123 + tag team2 +} +``` + +You can assign colors: + +```likec4 +specification { + tag deprecated { + color #FF0000 // or `rgb(255 0 0)` see below + } +} +``` + +You can add tags to element kinds: + +```likec4 +specification { + // Now every kafka-topic will be marked with the infra and data-lake tags + element kafka-topic { + #infra #data-lake + } + tag infra + tag data-lake +} +``` + +## Color + +Custom colors can be defined to extend built-in themes. Once defined in the specification, they can be used alongside the theme colors. + +```likec4 +specification { + color custom-color1 #F00 + color custom-color2 #AABBCC + color custom-color3 rgb(255, 0, 0) + color custom-color4 rgb(100 150 200) + color custom-color5 rgba(44, 8, 128, 0.9) + color custom-color6 rgba(255, 200, 100, 50%) + + element person { + style { + color custom-color1 + } + } +} +``` + +:::note +Only base colors with 3, 6, or 8-character hex codes, and `rgb()`/`rgba()` formats are supported. The reason is that to draw a node, we need not only a fill color but also a border color and text color. The same applies to the color of edges, where a line color, label background color, and text color are required. Therefore, LikeC4 generates a color palette used to build a color scheme from the provided base color. +::: + +# Styling + + +LikeC4 provides advanced customization capabilities. You can change colors, shapes, sizes, and icons of elements and relationships. + +## Element + +There are multiple ways to style elements: +- Style all elements of a kind in `specification` +- Specific for an element in `model` or `deployment` +- Override styles in `view` (more on this in [next section](/dsl/views/predicates/#style-predicates)) + +### Elements of a kind + +To style all elements of a kind, use the `style` block in the `specification`: + +```likec4 copy +specification { + element user { + style { + // every element of 'user' kind + shape person // has 'person' shape + color amber // and amber color + } + } + + element frontend { + style { + // every 'frontend' displayed as browser + shape: browser // ':' is optional, but if you prefer + } + } +} +``` + +### Single element + +To style a specific element, use the nested `style` block. +Element styles override ones from the kind: + +```likec4 copy +specification { + element actor { + style { + shape person + color red + } + } +} + +model { + customer = actor 'Customer' { + style { + // inherits shape and overrides color + color green + } + } +} +``` + +### Per view + +The [next section](/dsl/views/predicates/#style-predicates) explains how to customize elements per view. + +## Style properties + +### Shape + +```likec4 "shape person" copy +specification { + element actor { + style { + shape person + } + } +} +``` + +Available shapes: `rectangle` (default), `component`, `storage`, `cylinder`, `browser`, `mobile`, `person`, `queue`, `bucket`, and `document`. + + + +### Color + + +```likec4 "color red" copy +specification { + element actor { + style { + color red + } + } +} +``` + +Available colors: `primary` (default), `secondary`, `muted`, `amber`, `gray`, `green`, `indigo`, `red`. + + + +:::tip +It's also possible to use custom colors defined in [specification](/dsl/specification/#color). +::: + +### Size + +Size of an element is controlled by following properties: + +| property | explanation | +| :-------- | :----- | +| size | size of the shape | +| padding | space around element's title | +| textSize | font size of element's title | + +Each property accepts: `xsmall`, `small`, `medium`, `large`, or `xlarge` +(or short `xs`, `sm`, `md`, `lg`, `xl`). +Default size is `medium`. + +When shape size is `xsmall`, only element's title is displayed. + +```likec4 "size large" "textSize xl" +specification { + element element { + style { + size large + textSize xl + } + } +} +``` + + + +### Opacity + +If an element is displayed as a group (like a container), you can set its opacity: + +```likec4 "opacity 10%" +specification { + element element { + style { + opacity 10% + } + } +} +``` + + + + +### Border + +If an element is displayed as a group (like a container), you can change its border style: + +```likec4 "border dotted" +specification { + element element { + style { + opacity 10% + border dotted + } + } +} +``` + +Supported values: `dashed` (default), `dotted`, `solid`, `none` + + + +### Multiple + +To display element as multiple instances, set `multiple` to `true`: + +```likec4 +specification { + element element { + style { + multiple true + } + } +} + +``` + + + +### Icon + +Elements may have an icon - any browser-supported image format (png, svg, webp, etc.): + +```likec4 copy +model { + pg = service 'PostgreSQL' { + style { + // Publicly available with `https://` + icon https://icons.terrastruct.com/dev%2Fpostgresql.svg + + // or local image, relative to current file + icon ../postgresql.svg + } + } +} +``` + +:::tip +`icon` can be defined as a property and skip `style` block + +```likec4 copy +model { + pg = service 'PostgreSQL' { + icon https://icons.terrastruct.com/dev%2Fpostgresql.svg + } +} +``` +::: + +:::tip +Use `none` to unset `icon` + +```likec4 copy +pg = service 'PostgreSQL' { + icon none +} +``` +::: + +:::tip[Light and Dark mode SVG support] +When using SVGs, you can bake the light and dark modes into a single file and use a `prefers-color-scheme` media query to switch between them. + +LikeC4 will render the correct version based on the current theme. + +```xml + + + + + + + +``` + +Try changing the theme to see it in action! + +![Example of an icon with light and dark mode baked in](../../../assets/logo.svg) +::: + +### Bundled icons + +LikeC4 includes icons (over 5,000 in total) from these packs: +- `aws:` from aws-icons.com +- `azure:` from microsoft.com +- `bootstrap:` from Bootstrap Icons (2,000+ icons) +- `gcp:` from gcpicons.com +- `tech:` from techicons.dev and svglogos.dev + +Example: + +```likec4 copy +model { + fn = service 'Lambda Function' { + icon aws:lambda + } + k8s = service 'K8s Service' { + icon gcp:google-kubernetes-engine + } + pg = storage 'PostgreSQL' { + icon tech:postgresql + } + ui = component 'User Interface' { + icon bootstrap:house + } +} +``` + +
+ + + +
+ +:::note[Auto-derived technology] +Bundled icons (except `bootstrap:`) automatically set the element's `technology` if not already defined. +For example, `icon tech:docker` sets technology to **"Docker"**. See [Technology](/dsl/model#technology) for details. +::: + +:::tip +Use VS Code's code completion to explore available icons. +::: + + +### Icon color + +Use `iconColor` to override the icon color independently of the element fill. +Applies to bundled `bootstrap:*` icons. + +```likec4 copy +model { + api = service 'API' { + style { + icon tech:nodejs // ⛔️ Not applied + icon bootstrap:gear // ✅ Applies to `bootstrap:*` icons. + iconColor indigo + } + } +} +``` + +`iconColor` accepts theme colors or custom colors defined in the [specification](/dsl/specification/#color). + + + +### Icon size + +Use `iconSize` to resize the icon without changing the element size: + +```likec4 copy +model { + api = service 'API' { + style { + icon tech:nodejs + iconSize sm + } + } +} +``` + +`iconSize` uses the same values as [size](/dsl/styling/#size) and defaults to the element `size` (i.e. if not specified, but size is 'large', icon will be 'large'). + +### Icon position + +Use `iconPosition` to place the icon around the element text: + +```likec4 copy +model { + api = service 'API' { + style { + icon tech:nodejs + iconPosition top + } + } +} +``` + +Supported values: `left` (default), `right`, `top`, `bottom`. + + + + +### Aliased icons + +With `@` prefix, you can use aliased folders (learn more in [configuration](/dsl/config/#image-aliases)): + +```likec4 copy +model { + pg = service 'PostgreSQL' { + style { + // local images, based on aliased folders + icon @/postgresql.svg + } + } +} +``` + + +## Relationship + +There are multiple ways to style relationships: +- Style all relationships of a kind in `specification` +- Specific relationship in `model` +- Customize per `view` ([explained here](/dsl/views/predicates/#relationship-customization)) + +### Relationships of a kind + +Relationships can be styled in [specification](/dsl/relationships/#relationship-kinds): + +```likec4 +specification { + relationship async { + color amber + line dotted + head diamond + tail vee + } +} +``` + +### Specific Relationship + +```likec4 +model { + customer -> ui 'opens in browser' { + style { + line solid + color amber + } + } +} +``` + +### Relationship per view + +The [next section](/dsl/views/predicates/#relationship-customization) explains how to customize relationships per view. + +## Relationship properties + +Besides `color`, relationships may have the following properties: + +### Line + +| line | example | +| :----- | :-----: | +| dashed | .. | +| solid | .. | +| dotted | .. | + +By default, the line is `dashed`. + +### Arrow type + +The arrow type can be set for the head and the tail of the relationship: + +| type | example | +| :-------- | :-----: | +| normal | .. | +| onormal | .. | +| diamond | .. | +| odiamond | .. | +| crow | .. | +| vee | .. | +| open | .. | +| none | .. | + +> `onormal` means "outlined normal", i.e. no fill +> `odiamond` - "outlined diamond" + +By default, the head is `normal` and the tail is `none`. + +```likec4 +model { + customer -> ui 'opens in browser' { + style { + head diamond + tail crow + } + } +} +``` + +### Multiple + +By default, multiple relationships between the same two elements are merged into a single edge. +If the titles differ, the merged label becomes `[...]`. + +Set `multiple` to `true` to render each relationship as its own separate edge with its own label: + +```likec4 +specification { + relationship async { + multiple true + line dotted + color amber + } +} +``` + +You can also enable it per-view on individual relationships: + +```likec4 +views { + view flows { + include * + include customer -> api with { + multiple true + } + } +} +``` + +:::note +- `multiple true` splits matching relationships into individual edges; non-matching + relationships in the same connection remain merged. +- Per-view rules (`with { ... }`) take precedence over specification-level settings. + Set `multiple false` to disable expansion for specific relationships in a view. +- Expanded edges are never merged into bidirectional edges; only non-expanded + (merged) edges participate in bidirectional merging. +- Expansion from nested elements is supported — each relationship becomes its own edge + at the correct element level. +::: + +## Styles customization + +How to override default styles is explained in project configuration + + + +# Views + + +LikeC4 is model-based. Views are projections of the model from various perspectives, scopes, and levels of detail, such as: + +- System/service overviews +- Component interactions in specific use cases +- Data flows and sequence diagrams + +LikeC4 does not enforce specific rules, such as a strict number of levels or what should be included — it's entirely up to you and your context. + +## View definition + +Views are defined in `views` section. +They can be named (must be unique) or unnamed (can't be referenced, but still can be exported): + +```likec4 +views { + // with name + view index { + } + // unnamed + view { + } +} +``` + +The view name is used as the image filename during export and as part of the URL when sharing, so it's advisable to define one. + + + +### View properties + +Views can have a `title`, `description`, `tags` and `links`: + +```likec4 +views { + + view epic12 { + #next, #epic-12 + title "Cloud System - Changes in Epic-12" + + // Description can be Markdown with triple quotes + description """ + This diagram shows the **high-level** + components and interactions. + """ + + link https://my.jira/epic/12 'Epic-12' + + } + +} +``` + +Properties must be defined before any [predicates](/dsl/views/predicates/). + +## Scoped views + +A view can be defined for a specific element (`view of ...`). +The view will then inherit the [scope](/dsl/references#scope) of that element: + +:::note +You will learn about `include` and `exclude` in the [predicates](/dsl/views/predicates/) section. +::: + +```likec4 +views { + + view { + include api // ⛔️ Error: 'api' is not found + } + + view of cloud.backend { + include api // ✅ This is OK, resolves to 'cloud.backend.api' + } + + view of legacy { + include api // ✅ This is OK, resolves to 'legacy.api' + } + +} +``` + +Additionally, a scoped view becomes the default for the element: + +```likec4 +views { + + view { + // on click navigates to 'view1', + // because it is default for 'cloud.backend' + include cloud.backend + } + + view view1 of cloud.backend { + include * + } + +} +``` + +You can define multiple views for the same element, with the default determined by their order. + +## Extend views + +Views can be extended to avoid duplication, to create a "baseline" or, for example, "slides" for a presentation: + +```likec4 +views { + + view view1 { + include * + } + + view view2 extends view1 { + title 'Same as View1, but with more details' + + style * { + color muted + } + + include some.backend + } + + // cascade inheritance + view view3 extends view2 { + title 'Same as View2, but with more details' + + include * -> some.backend + } + +} +``` + +The predicates and style rules of extended views are applied after the ones from ancestors. + +Extended view also inherits the scope: + +```likec4 +views { + + view view1 of cloud.backend { + title 'Backend components' + } + + view view2 extends view1 { + include api // ✅ This is OK, references 'cloud.backend.api' + } + +} +``` + +# Generated Views + + +### Relationships browser + +### Relationship decomposition + +# Dynamic views + + + +Dynamic view describes a particular use-case or scenario, with specific elements and interactions, defined only in the view (without polluting the model). + +## Dynamic view definition + +```likec4 showLineNumbers copy collapse={1-54} +//dynamic-view.c4 +specification { + element actor { + style { + shape person + } + } + element system + element component +} + +model { + customer = actor 'Customer' { + description 'Customer of Cloud System' + } + + cloud = system 'Cloud System' { + backend = component 'Backend' { + description 'Backend services and API' + + auth = component 'Authentication' + + api = component 'Backend API' { + description 'RESTful API' + } + + api -> auth 'validates bearer token' + } + + ui = component 'Frontend' { + description ' + All the frontend applications + of Cloud System + ' + style { + shape browser + } + + web = component 'Customer Dashboard' { + description 'React Application' + style { + shape browser + } + } + + web -> auth + web -> api 'requests' + } + } + + customer -> web 'opens in browser' + +} + +views { + dynamic view example { + title 'Dynamic View Example' + customer -> web 'opens in browser' + web -> auth 'updates bearer token if needed' + web -> api 'POST request' + api -> auth // title is derived from the model + api -> api 'process request' // allow self-call + + // reverse direction, as a response to line 59 + web <- api 'returns JSON' + + // Include elements, that are not participating + include cloud, ui, backend + + style cloud { + color muted + opacity 0% + } + } +} +``` + +### Continuous steps + +Alternative syntax for describing continuous steps: `A -> B -> C` + +```likec4 copy +dynamic view example { + customer + -> web + -> api // same as web -> api + -> web // same as web <- api +} +``` + +It identifies the backward direction of the step, i.e. `A -> B -> A` is the same as `A -> B; A <- B`. +Nested steps are processed as well, i.e. +```likec4 +A -> B -> C -> D -> B -> A +``` +is the same as +```likec4 +A -> B +B -> C +C -> D +D -> B +A <- B // is backward +``` + + +### Flow control + +Steps can be grouped into flow-control blocks to express parallelism, loops, optional paths, alternatives, and error handling. Every block accepts an optional title and can be nested. + +:::caution[Experimental] +Flow control blocks are experimental — syntax and rendering may still change. +We are looking for your feedback in discussions. +::: + +In the `sequence` variant these blocks are rendered as nested frames (`par`, `opt`, `loop`, …), mirroring classic sequence-diagram fragments. + +#### Parallel — `parallel` / `par` + +Steps inside run concurrently: + +```likec4 copy +dynamic view parallelexample { + title 'Dynamic View Parallel Example' + ui -> api + parallel { + api -> cache + api -> db + } + // or + par { + api -> cache + api -> db + } +} +``` + +Nested parallel blocks are not possible - see this discussion + +#### Optional — `opt` + +A block of steps that may be skipped: + +```likec4 copy +opt 'if not cached' { + api -> db 'load and cache' +} +``` + +#### Loop — `loop` + +A block of steps that repeats: + +```likec4 copy +loop 'until success' { + api -> auth 'retry authentication' +} +``` + +#### Break — `break` + +Interrupts the enclosing flow (e.g. exits a loop): + +```likec4 copy +loop 'poll for result' { + api -> db 'check status' + break 'when ready' { + api -> web 'return result' + } +} +``` + +#### Alternatives — `alt` + +Groups mutually exclusive branches. Each branch is a `when` / `if` / `else` block with an optional title: + +```likec4 copy +alt { + when 'authorized' { + web -> api 'requests data' + } + else 'not authorized' { + web -> customer 'shows login' + } +} +``` + +#### Try / catch / finally — `try` + +Models the happy path together with error handling. `catch` and `finally` are optional: + +```likec4 copy +try { + api -> db 'query' +} catch 'on failure' { + api -> web 'shows error' +} finally { + api -> api 'release resources' +} +``` + +#### Nesting + +Blocks can be combined and nested to any depth: + +```likec4 copy +alt { + when 'online' { + loop 'until synced' { + web -> api 'sync changes' + } + } + else 'offline' { + web -> web 'queue locally' + } +} +``` + +The `sequence` variant below combines `alt` / `when` / `else`, `loop`, `try` / `catch` / `finally`, `parallel` and `opt`: + + + +### Navigation + +Steps can navigate to other dynamic views: + +```likec4 copy +dynamic view level1 { + title 'Highlevel' + + ui -> api { + navigateTo moreDetails + } +} + +dynamic view moreDetails { + title 'Some details' +} +``` + +### Notes + +`notes` can be used to add additional information to the step. It supports Markdown: + +```likec4 copy +dynamic view stepnotes { + title 'Dynamic View Parallel Example' + + ui -> api { + notes ' + 🏛️ - Requests data using predefined GraphQL queries + 🤖 - Queries regression on CI + ' + } + + parallel { + api -> cache { + // Supports Markdown + notes ''' + **What it does**: + - requests session-scoped data + - updates TTL + + ''' + } + } +} +``` + +## Variants + +Dynamic views support two variants: `diagram` and `sequence`. +By default, dynamic views are displayed as diagrams. + +### Diagram + +![diagram variant](../../../../assets/views/dynamic-variant-diagram.png) + +### Sequence + +Classic sequence diagram: + +![sequence variant](../../../../assets/views/dynamic-variant-sequence.png) + +:::note +The sequence variant supports only connections with _leaf_ elements, i.e. elements that do not have any child elements. +::: + +## Order of actors + +The sequence variant allows to set the order of actors with `include` predicate: + +**Default variant**: + +```likec4 copy +dynamic view order1 { + customer + -> web + -> auth + -> web + -> api +} +``` +Steps define the order of actors. + +![diagram sequence-order-1](../../../../assets/views/sequence-order-1.png) + +**Ordered variant:** + +```likec4 copy {9-12} +dynamic view order2 { + customer + -> web + -> auth + -> web + -> api + + // Strict order + include + auth, + web, + api +} +``` + +`include` predicate may define order partially, for the rest order will be derived based on the steps. + +![diagram sequence-order-2](../../../../assets/views/sequence-order-2.png) + +## Example + +Browse this example: + + + +
+ + + + +# Organize views + + +You can organize views into folders to keep the workspace clean and easy to navigate. + +## Create folders + +To create a folder use `/` in the title. +For example, the following view will have the title `Production`, and will be nested under `Deployment` folder: + +```likec4 +views { + view { + title 'Deployment / Production' + } +} +``` + +On the UI, this view will be displayed as: + +![organize views](../../../../assets/views/organize.png) + +Views can be defined in the same file, but placed in different folders: + +```likec4 +views { + dynamic view { + title 'Use Cases / 16.2 Checkout / Checkout flow' + } + + deployment view { + title 'Deployments / Staging / Checkout microservice' + } +} +``` + +## Common folder + +You can specify a common folder for the `views` block. +Every view defined inside will be placed under that folder: + +```likec4 "'Domain 1 / Subdomain'" +// Common folder for all views in the block +views 'Domain 1 / Subdomain' { + + view { + // Will be displayed as 'Domain 1 / Subdomain / Landscape' + title 'Landscape' + } + + dynamic view { + // You can add nested folder + // Will be displayed as 'Domain 1 / Subdomain / Use Cases / 16.2 Checkout' + title 'Use Cases / 16.2 Checkout' + } +} +``` + +# View Predicates + + +Views are not static, they are generated from the model. Any changes in the model are applied immediately and update views. +Two types of predicates define what is visible: element and relationship predicates. + + + +## Element predicates + +Element predicates explicitly define which elements are visible. Each included element brings in its relationships with already visible elements. + +```likec4 +view { + // Only backend is visible + include backend + + // Add frontend to the view + // and its relationships with backend + include frontend + + // Add authService to the view + // and its relationships with visible (backend and frontend) + include authService + + // Add children of messageBroker, + // and their relationships among themselves and visible (backend, frontend and authService) + include messageBroker.* + + // Add all descendants of messageBroker, + // and their relationships among themselves and visible (backend, frontend and authService) + include messageBroker.** + + // Exclude emailsQueue and its relationships + exclude messageBroker.emailsQueue +} +``` + + + +### Combining + +Predicates can be combined. The following is the same as example above: + +```likec4 +view { + include + backend, + frontend, + authService, + messageBroker.** + + exclude messageBroker.emailsQueue +} +``` + +### Wildcard + +Wildcard predicates can be used to reference "everything" (but it differs for scoped/unscoped views). +Consider the following model: + +```likec4 +model { + actor customer { + -> webApp 'uses in browser via HTTPS' + } + system cloud { + container backend { + component api + } + container ui { + component webApp { + -> api 'requests data' + } + } + } +} +views { + + // Unscoped view - wildcard refers to top-level elements + view { + include * + // Visible top-level elements: customer, cloud + // and derived relationship customer -> cloud + } + + // Scoped view - wildcard refers to element and its children + view of cloud.ui { + include * + // Visible: + // - cloud.ui + // - cloud.ui.webApp + // - customer + // - relationship customer -> cloud.ui.webApp + // - cloud.backend + // - cloud.ui.webApp -> cloud.backend, derived from cloud.ui.webApp -> cloud.backend.api + } +} +``` + +### With overrides + +You can modify element properties specifically for the view: + +```likec4 +// Include the element and override its properties +include cloud.backend with { + title 'Backend components' + description '...' + technology 'Java, Spring' + icon tech:java + color amber + shape browser + multiple true +} +// Include all nested elements, change color and textSize +include cloud.* with { + color amber + textSize small +} +``` + +`with` may be used only within `include`. + +### With custom navigation + +You can define custom navigation and links between views: + +```likec4 title="example.c4" + +view view2 { + include * + include cloud.backend with { + // navigate to 'view3' on click + navigateTo view3 + } +} + +view view3 { + include * + include cloud.backend with { + // navigate back to 'view2' + navigateTo view2 + } +} +``` + +### By element kind or tag + +```likec4 +// elements by kind +include element.kind != system +exclude element.kind = container + +// elements by tag +include element.tag != #V2 +exclude element.tag = #next +``` + +:::caution +These predicates may be deprecated in the future, please consider [`where`](#filter) operator +::: + +### Element Selectors + +#### Children `.*` + +The children selector includes element's children and their relationships with visible elements. + +```likec4 +include cloud.* + +// Same as +include cloud.backend +include cloud.ui +``` + +#### Descendants `.**` + +The descendants selector includes element's descendants **IF** they have a relationship with visible elements. + +```likec4 +include cloud.** + +// Same as +include cloud.backend +include cloud.ui +include cloud.ui.webApp +``` + +#### Expand `._` + +The expand selector includes element's children **IF** they have a relationship with visible elements. +All other children are omitted. + +```likec4 +include cloud._ + +// Same as +include cloud +include -> cloud.* -> +``` + +## Relationship predicates + +Relationship predicates include elements only if they have relationships that meet the specified predicate conditions. + +### Directed relationships + +Include elements if they have **directed** relationships (or their nested elements): + +```likec4 +// Include customer and cloud: +include customer -> cloud + +// Include customer and nested elements of cloud (that have relationships): +include customer -> cloud.* +``` + +### Any relationship + +Include elements if they have any relationships: + +```likec4 +include customer <-> cloud +``` + +### Incoming + +Include elements if they have incoming relationships from already visible elements. +Here’s an example based on the model from the [wildcard example](#wildcard): + +```likec4 wrap title="incoming predicate.c4" +view { + // visible element + include customer + + // include nothing, customer has no relation to backend + include -> backend + + // add ui, + // because customer has a relationship with nested ui.webApp + include -> ui + + // add backend, because visible ui has a relationship to backend + // derived from ui.webApp -> backend.api + include -> backend +} + +// This view includes customer and ui +view { + include + customer, + -> cloud.* +} +``` + +:::tip +Relationship predicates are useful for refining your diagrams, allowing you to narrow the scope and focus on specific parts of the system. +::: + +### Outgoing + +Include elements if only they have outgoing relationships to already visible elements: + +```likec4 +include customer -> +include cloud.* -> +``` + +### In/Out + +Include nested elements of `cloud`, that have any relationships with visible elements: + +```likec4 +include -> cloud.* -> +``` + +### Relationship customization + +Relationships can be customized inside view: + +```likec4 +include + // Make lines red and solid + cloud.* <-> amazon.* with { + color red + line solid + }, + // or only directed + customer -> cloud.* with { + // Override label + title 'Customer uses cloud' + navigateTo dynamicview1 + }, +``` +:::tip +Sometimes, connections may have a title `[...]`. This indicates that the connection has been merged from multiple relationships with different titles, and it was impossible to derive a definitive one. +You can change the title, or set `multiple true` to render each relationship as a separate edge: + +```likec4 +include + customer -> cloud with { + // Render each relationship separately + multiple true + }, + // or override the merged title + customer -> cloud with { + title 'Customer uses cloud' + }, +``` +::: + +:::caution +It is possible to customize relationships with known endpoints only +(i.e. [directed](#directed-relationships) or [any between](#any-relationship)) + +For example, `* -> *` can be customized (in other words, all relationships on the view), but outgoing `cloud.* ->` can not. +::: + +### Relationship navigation + +To customize [navigation](/dsl/relationships/#navigate-to) from relationship: + +```likec4 +include + webApp -> backend.api with { + navigateTo dashboardRequestFlow + } +``` + +## Filter + +`where` operator narrows down results by applying additional conditions: + +```likec4 +// include only microservices from nested +include cloud.* + where kind is microservice + +// only microservices and not deprecated +include cloud.* + where + kind == microservice and // possible to use 'is' or '==' + tag != #deprecated // possible to use 'is not' or '!=' + +// Use logical operators +include cloud.* + where + not (kind is microservice or kind is webapp) + and tag is not #legacy + and (tag is #v1 or tag is #v2) +``` + +
+ +**Relationship predicates** + +When `where` is used with element predicates, it is applied to the elements. +When used with relationship predicates - to the relationships. + +```likec4 +include + // only relationships with tag #messaging + cloud.* <-> amazon.* + where tag is #messaging, + + // only incoming http-requests + -> backend + where kind is http-request + -[http-request]-> backend + .http-request backend +``` + +It is also possible to filter relations by tag or kind of its endpoints. +```likec4 +include + // only relationships outgoing from elements with with tag #next + cloud.* -> amazon.* + where source.tag is #next, + + // only incoming relations of elements with kind microservice + -> * + where target.kind is microservice +``` + +
+ +**Together with `with`** + +It is possible to use `where` together with `with`, but `where` should be defined first: + +```likec4 +include * + where kind is microservice + with { + color amber + } +``` + +
+ +:::tip +Less verbose and more satisfying results are achieved with `where` in `exclude` predicates. +For example: + +```likec4 + +// only keep elements tagged with #v1 +exclude * where tag is not #v1 + +// only keep relationships tagged with #commands +exclude * -> * where tag is not #commands + +``` + +Together with [predicate groups](#global-predicate-groups) you may define a "baseline" (includes everything), and then filter out in inherited views. + +::: + +### Metadata filter + +`where` can also filter by element or relationship metadata values: + +```likec4 +// include only elements with environment="production" +include cloud.* + where metadata.environment is "production" + +// exclude elements without a version metadata key +exclude * + where not metadata.version + +// combine with other filters +include cloud.* + where + metadata.environment is "production" + and kind is not database +``` + +Boolean metadata values can be matched with `true` or `false` directly (no quotes needed): + +```likec4 +// matches elements where critical is true +include * + where metadata.critical is true +``` + +When a metadata value is an array (e.g. `regions ['us-east-1', 'eu-west-1']`), `is` checks if the array **contains** the value: + +```likec4 +// matches if "us-east-1" is one of the regions +include * + where metadata.regions is "us-east-1" +``` + +For relationship predicates, filter by the relationship's own metadata or by its endpoints: + +```likec4 +include + // only relationships with protocol="grpc" + cloud.* -> amazon.* + where metadata.protocol is "grpc", + + // only relations from elements with env="production" + cloud.* -> * + where source.metadata.environment is "production", + + // only relations to staging targets + * -> * + where target.metadata.environment is "staging" +``` + +The same metadata filters work with `exclude` predicates: + +```likec4 +// remove relationships with protocol="http" +exclude * -> * + where metadata.protocol is "http" + +// remove relationships targeting staging elements +exclude * -> * + where target.metadata.environment is "staging" +``` + +In deployment views, `source.metadata.*` and `target.metadata.*` follow the deployment metadata rules. +Metadata defined on a deployed instance replaces the metadata from its model element. + +## Global predicate groups + +If you find yourself repeating the same predicates in multiple views, you can define them as global group: + +```likec4 +global { + predicateGroup microservices { + include cloud.* + where kind is microservice + exclude * + where tag is #deprecated + } +} + +views { + view of newServices { + include cloud.new.* + global predicate microservices + } + + view of newBackendServices { + // Keep in mind that order is significant + global predicate microservices + include cloud.backend.* + } +} +``` + +## Groups + +It is possible to group elements, and this is rendered as a boundary around them: + +```likec4 +view { + + group { + include backend + } + + // with title + group 'Frontend' { + include frontend.* + } + + // with style + group 'Service Bus' { + color amber + opacity 20% + border solid + + include messageBroker.* + } +} +``` + +Groups can be nested: + +```likec4 +view { + group 'Third-parties' { + group 'Integrations' { + group 'Analytics' {} + group 'Marketing' {} + } + group 'Monitoring' {} + } +} +``` +:::note +Order of predicates is significant. +
+How element predicates are grouped? + +For element predicates - element stays in first group it was included. + +```likec4 +group { + include backend //wins + group { + include backend //ignored + } +} +group { + group { + include api //wins + } + include api //ignored +} +``` +It is possible to change: + +```likec4 +group { + include backend + group { + exclude backend + include backend //wins + } +} +``` +
+
+How relationship predicates are grouped? + +For relationship predicates - the last one "wins": + +```likec4 +group { + include -> backend + group { + include -> backend //wins + } +} + + +group { + group { + include -> backend + } + include -> backend //wins +} +``` +
+::: + + + +## Style predicates + +Style predicates define how elements are rendered, and applied in the order they are defined merging with previous ones: + +```likec4 +view apiApp of internetBankingSystem.apiApplication { + + include * + + // apply to all elements + style * { + color muted + opacity 10% + } + + // apply only to these elements + style singlePageApplication, mobileApp { + color secondary + size xlarge + } + + // apply only to nested of apiApplication + style apiApplication.* { + color primary + multiple true + } + + // apply to apiApplication and nested + style apiApplication._ { + color primary + } + + // apply only to elements with specific tag + style element.tag = #deprecated { + color muted + } + + // apply to elements not tagged + style element.tag != #deprecated { + opacity 20% + } +} +``` + +:::caution +[`Group`](#groups) does not support nested `style` predicates (yet). +::: + +### Shared local styles + +Styles can be shared within `views` block (_"local styles"_): + +```likec4 +views { + // apply to all views in this block + style * { + color muted + opacity 10% + } + + view of apiApp { + include * + style cloud.web.* { + color green + } + } + + view of mobileApp { + include * + style cloud.ui.* { + color amber + } + } +} + +views { + // Styles from previous block are not applied here + // ... +} +``` + + + +:::caution +[Overrides](#with-overrides) are always applied last, after all styles +::: + + +### Shared global styles + +Styles can be shared globally. +Global styles must be named and defined in `global` block: + +```likec4 {4,9,15,25,33-34} +global { + // Format: + // style { ... } + style mute_all * { + color muted + opacity 10% + } + + style applications + singlePageApplication._, + mobileApp._ { + color secondary + } + + style mute_deprecated + element.tag = #deprecated { + color muted + } +} + +views { + view of singlePageApplication { + // Styles are applied in the order they are defined + // 1. Apply global style + global style mute_all + + // 2. Then this + style cloud.* { + color green + } + + // 3. and 4. + global style applications + global style mute_deprecated + } +} +``` + +### Shared style groups + +Global styles can be grouped: + +```likec4 {3,18} +global { + // Define style group + styleGroup common_styles { + style singlePageApplication, mobileApp { + color secondary + } + style element.tag = #deprecated { + color muted + } + } +} + +views { + view mobileApp of mobileApp { + include * + + // Apply styles from group + global style common_styles + + // Override + style mobileApp { + color primary + } + } +} +``` + +:::tip +Global styles and groups can be used as `views`-locals: + +```likec4 +global { + style mute_all * { color muted } + styleGroup theme1 { //... + styleGroup theme2 { //... +} + +// All views within this block have styles from 'theme1' +views { + global style theme1 + view view1 { //... +} + +// All views have 'mute_all' style and all from 'theme2' +views { + global style mute_all + global style theme2 + view view2 { //... +} +``` +::: + + +## Auto-layout + +```likec4 +view { + include * + autoLayout LeftRight 120 110 +} +``` + +Parameters are: +- direction: possible values are `TopBottom` (default), `BottomTop`, `LeftRight`, `RightLeft`. +- rank distance: optional, must be a positive number +- node distance. optional, must be a positive number + + + + +## Extend views + +Views can be extended to avoid duplication, to create a "baseline" or, for example, "slides" for a presentation: + +```likec4 +views { + + view view1 { + include * + } + + view view2 extends view1 { + title 'Same as View1, but with more details' + + style * { + color muted + } + + include some.backend + } + + // cascade inheritance + view view3 extends view2 { + title 'Same as View2, but with more details' + + include * -> some.backend + } + +} +``` + +The predicates and style rules of extended views applied after the ones from ancestors. + +Extended view also inherits the scope: + +```likec4 +views { + + view view1 of cloud.backend { + title 'Backend components' + } + + view view2 extends view1 { + include api // ✅ This is OK, references 'cloud.backend.api' + } + +} +``` +## Rank constraints + +You can keep specific elements on the same horizontal/vertical level (or push them to the beginning/end of the layout) +by adding an explicit `rank` block to the view. +These rank constraints are forwarded to the Graphviz layout engine to achieve the desired layout effects. + +```likec4 +view checkoutFlow { + include * + + // keep the API nodes aligned + rank same { + cloud.backend.api, + cloud.backend.billingApi, + } + + // make customers appear at the beginning of the diagram, exclusive of the elements + rank source { + customer + } + + // render reporting systems at the end, exclusive of the elements + rank sink { + analytics, + dataWarehouse + } +} +``` + +- Allowed rank values: `same`, `min`, `max`, `source`, `sink`. If omitted, `same` is assumed. +- Targets are regular `FqnRef`s, so you can reference nested elements just like in other rules. Non-existent or duplicate targets are ignored. +- The constraint only affects elements that actually remain in the computed view. If a predicate later removes an element, it also disappears from the rank block. +- Rank rules participate in manual tiling of compound nodes as well, so authored constraints combine with the automatic layout instead of fighting it. + +Use rank constraints sparingly—they are most helpful for anchoring critical columns/rows +(e.g., ingress vs. egress nodes, semantic grouping in lieu of container) to achieve better layout. + +# Deploy to GitHub Pages + +:::note +You can check likec4/template repository. +It builds and deploys static website to GitHub Pages + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/~/github.com/likec4/template) +::: + +## Use LikeC4 CLI + +Prefer to use [LikeC4 CLI](/tooling/cli) directly in your scripts, as it offers more control. +Example workflow (it uses `bun` for faster setup, but you can use your preferred runtime): + +```yaml +# Sample workflow for building and deploying a website to GitHub Pages +name: Deploy Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_call: + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in progress and the latest queued. +# However, do NOT cancel in-progress runs, as we want to allow these production deployments to be completed. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build-pages: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: oven-sh/setup-bun@v2 + + - uses: actions/configure-pages@v4 + id: pages + + - name: build + run: | + bunx likec4@latest build \ + --base "${{ steps.pages.outputs.base_path || '/' }}" \ + --output dist + + - name: upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./dist + + # Deployment job + deploy-pages: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build-pages + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + +``` + +## GitHub Actions workflow + +Use [LikeC4 GitHub Action](/tooling/github/) to build and deploy static website to GitHub Pages. + +```yaml +# Sample workflow for building and deploying a website to GitHub Pages +name: Deploy Pages + +on: + # Runs on pushes targeting the default branch and c4 files + push: + branches: ["main"] + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in progress and the latest queued. +# However, do NOT cancel in-progress runs, as we want to allow these production deployments to be completed. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build-pages: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Pages + id: pages + uses: actions/configure-pages@v4 + + - name: Build + uses: likec4/actions@v1 + with: + action: build + output: dist + # required if you don't set a custom domain for the repository + # https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages#types-of-github-pages-sites + base: ${{ steps.pages.outputs.base_path }} + likec4-version: latest + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./dist + + # Deployment job + deploy-pages: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build-pages + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 +``` + +# Embed to website + + + + This page is under construction. + + +# Preview changes in PR + + + +# Deploy a static website + +import { PackageManagers } from 'starlight-package-managers'; + +## Pre-requisites + +You must have [Node.js](https://nodejs.org) installed. +Minimum supported version is 20.x, but using the latest stable version is recommended. + +You can also use [LikeC4 Docker](/tooling/docker/). + +## Build + +Run `build` command, that prepares a folder with static files ready to be deployed to any host. + +```sh +npx likec4 build -o ./dist +``` + +Available options: + +| Option | Description | +| ----------------------- | ----------------------------------------------------------------------------------------------------- | +| `-o, --output` | Output directory | +| `--base` | Base URL from which the app is being served, e.g., "/", "/pages/", or "./" for a relocatable app | +| `--use-hash-history` | Hash-based navigation, e.g. "/#/view" instead of "/view" | +| `--use-dot` | Use local binaries of Graphviz ("dot") instead of bundled WASM | +| `--title` | Base title of the app pages (default is "LikeC4") | +| `--output-single-file` | Generates a single self-contained HTML file | + +To see all available options run: +```sh +npx likec4 build -h +``` + + +## Deploy + +The LikeC4 CLI uses Vite to build the website. + +All the examples from [Vite documentation](https://vitejs.dev/guide/static-deploy.html) apply to LikeC4, just replace `vite build` with `likec4 build`. + +# Enforce and validate your model + +import { PackageManagers } from 'starlight-package-managers'; + +Sometimes you may want to enforce custom rules on your model or validate its consistency. +Here's a simple recipe for how to do that. + +:::note +Check likec4/template repository for a complete example. + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/~/github.com/likec4/template) +::: + + +## Test your model + +In this example we'll use [Vitest](https://vitest.dev) together with the [LikeC4 API](/tooling/model-api/). +The LikeC4 API gives you methods to query and traverse the model - perfect for writing tests that enforce your rules. + + + +### Example + +Suppose we want to enforce that every element of kind `app` has a `technology` specified. + +```ts +// test/metadata.spec.ts +import { LikeC4 } from './LikeC4' +import { test } from 'vitest' + +// Initialize and compute LikeC4 Model +const likec4 = await LikeC4.fromWorkspace('..') +const model = likec4.computedModel() + +// With `test.for` we generate tests for each element of kind `app` +// This improves the output, showing each test failure separately +test.for( + model + // Select elements of kind `app` + .elementsWhere({ kind: 'app' }) + // Map to array of [id, element] tuples, we need it for test names + .map(e => [e.id, e] as const) + .toArray(), +)('app "%s" has technology', ([, e], { expect }) => { + expect(e.technology).toBeTruthy() +}) + +// Or we can use `expect.soft` to accumulate all errors +test('elements of kind `app` have technology', ({ expect }) => { + expect.hasAssertions() + for (const app of model.elements()) { + if (app.kind !== 'app') continue // Skip non-app elements + expect.soft(app.technology, `app ${app.id} has no technology`).toBeTruthy() + } +}) +``` + +:::note +Calls to `LikeC4.fromWorkspace('..')` are memoized (by absolute path). +You can call it multiple times without performance impact - for example in `beforeEach`. + +You can also use Vitest's [`provide`](https://vitest.dev/advanced/api/vitest.html#provide) function to reuse the same workspace path. +::: + + +## Pre-generate model + +We can optimize our tests by pre-generating the model in [global setup](https://vitest.dev/config/#globalsetup): + +```ts +// global-setup.ts +import { execSync } from 'node:child_process' + +export default function() { + execSync('npx likec4 gen model -o ./test/likec4-model.ts', { + stdio: 'inherit' + }) +} +``` + +The generated model is fully typed, giving us type checking and autocompletion in tests: + +```ts +// test/metadata.spec.ts +import { likec4model } from './likec4-model' +import { test } from 'vitest' + +test('Relationships should have metadata', ({ expect }) => { + expect.hasAssertions() + for (const r of likec4model.relationships()) { + expect.soft( + r.getMetadata('key'), // here we get type checking + `Relationship ${r.source.id} -> ${r.target.id} has no metadata` + ).toBeDefined() + } +}) +``` + +We can go further and use [test context](https://vitest.dev/api/#test-context) to improve our experience: + +```ts +// test/likec4test.ts +import { likec4model } from './likec4-model' +import { test } from 'vitest' + +interface LikeC4TestFixtures { + likec4: typeof likec4model +} + +// This wil be our test function with the model in the context +export const likec4test = test.extend({ + likec4: async ({}, use) => { + await use(likec4model) + }, +}) +``` + +Now refactor tests to use it: + +```ts +// test/metadata.spec.ts +import { likec4test } from './likec4test' + +likec4test('Relationships should have metadata', ({ expect, likec4 }) => { + expect.hasAssertions() + for (const r of likec4.relationships()) { + expect.soft( + r.getMetadata('key'), // here we get type checking + `Relationship ${r.source.id} -> ${r.target.id} has no metadata` + ).toBeDefined() + } +}) +``` + +## Conclusion + +This approach makes it easy to enforce custom constraints and validate your model consistency. +Running these checks in CI pipeline is fast and provides immediate feedback when the model breaks your rules. + +# LikeC4 + + + +# AI Tools + + +LikeC4 provides two complementary tools for AI-powered development: **Agent Skills** for teaching AI assistants the DSL syntax, and an **MCP Server** for querying your architecture model. + +## Agent Skills + +Agent skills are DSL references that AI coding assistants load automatically when editing `.c4`/`.likec4` files. +They teach agents correct LikeC4 syntax, patterns, and validation workflows — so they can write DSL code without hallucinating. + +### Install into any project + +```sh +npx skills add https://likec4.dev/ +``` + +This uses the Agent Skills Discovery protocol. Available skills: + +| Skill | Description | +|-------|-------------| +| `likec4-dsl` | Complete DSL reference — specification, model, views, deployment, predicates, examples | + +Works with Claude Code, Cursor, Windsurf, and other agents that support the protocol. + +## MCP Server + +LikeC4 MCP Server provides knowledge of your LikeC4 model to LLMs. +This enables you to query your model in natural language: + +> _"Lookup LikeC4 model and list all incoming relationships of the backend api"_ + +> _"What nested elements of the 'Backend' have relations with the legacy api"_ + +> _"List all elements tagged legacy from team1 project"_ + +> _"What technologies are used for ui (consider all elements with browser shape)"_ + +> _"Export to CSV all relationships between Backend and Amazon SQS"_ + +### Usage + +Three options are available: +- Use extension's built-in MCP Server +- Use `likec4 mcp` CLI +- Use `@likec4/mcp` package + +#### Using extension + +When [LikeC4 extension](/tooling/editors/) is installed in VSCode, the MCP Server is registered automatically via VSCode's native MCP support (stdio transport). +No additional configuration is needed — the server appears in your MCP server list once the extension is active. + +:::caution +The MCP server is only available when the extension is active. +The extension activates automatically once you open any LikeC4 source file. + +If it doesn't show up, try refreshing the list of available MCP servers in your editor. +::: + +For other editors, start the MCP server via CLI and configure it manually: + + + + + Create `.cursor/mcp.json`: + + ```json + { + "mcpServers": { + "likec4": { + "url": "http://localhost:33335/mcp" + } + } + } + ``` + + Then start the server: `likec4 mcp --http` + + + + + See [Windsurf documentation](https://docs.windsurf.com/windsurf/mcp) for details: + + ```json + { + "mcpServers": { + "likec4": { + "serverUrl": "http://localhost:33335/mcp" + } + } + } + ``` + + Then start the server: `likec4 mcp --http` + + + + + Add the MCP server to Claude Code: + + ```sh + claude mcp add likec4 -- npx -y @likec4/mcp + ``` + + Or add to `.mcp.json`: + + ```json + { + "mcpServers": { + "likec4": { + "command": "npx", + "args": ["-y", "@likec4/mcp"], + "env": { + "LIKEC4_WORKSPACE": "${workspaceFolder}" + } + } + } + } + ``` + + + + +#### Using CLI + +If you have installed [`likec4`](/tooling/cli) CLI, you can start MCP server with `stdio` transport: + +```sh +likec4 mcp +# or +likec4 mcp --stdio +``` + +Start MCP server with `http` transport on port `33335` (default) at `./src` folder: + +```sh +likec4 mcp --http ./src +``` + +Start MCP server with `http` transport on port `1234`: + +```sh +likec4 mcp -p 1234 +``` + +#### Using `@likec4/mcp` package + +Example configuration: + +```json +{ + "mcpServers": { + "likec4": { + "command": "npx", + "args": [ + "-y", + "@likec4/mcp" + ], + "env": { + "LIKEC4_WORKSPACE": "${workspaceFolder}" + } + } + } +} +``` + +This package starts MCP server using [`stdio`](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#stdio) transport. + +If `LIKEC4_WORKSPACE` environment variable is not set, the current directory will be used as workspace. + +:::tip +You can also use `@likec4/mcp` package as CLI (this package is smaller than `likec4`): + +```sh +npm install -g @likec4/mcp + +# Start MCP server with streamable http transport +likec4-mcp --http --port 1234 /path/to/workspace + +# See available options +likec4-mcp -h +``` + +Disable watch mode with `--no-watch` to consume less resources, if you have static workspace. +::: + +### Available tools + +| Tool | Description | +|------|-------------| +| `list-projects` | List all LikeC4 projects in the workspace | +| `read-project-summary` | Project specification, configuration, all elements, deployment nodes and views | +| `search-element` | Search elements and deployment nodes by id/title/kind/shape/tags/metadata | +| `read-element` | Full element details including relationships, views, deployments, metadata | +| `read-deployment` | Details of a deployment node or deployed instance | +| `read-view` | Full view details (nodes/edges) and source location | +| `find-relationships` | Direct and indirect relationships between two elements | +| `query-graph` | Query element hierarchy (ancestors, descendants, siblings, children, parent) and single-hop relationships (incomers, outgoers) | +| `query-incomers-graph` | Recursive BFS graph of all upstream dependencies/producers | +| `query-outgoers-graph` | Recursive BFS graph of all downstream consumers/dependents | +| `query-by-metadata` | Search elements by metadata key-value pairs (exact/contains/exists) | +| `query-by-tags` | Tag filtering with boolean logic (allOf, anyOf, noneOf) | +| `query-by-tag-pattern` | Search elements by tag patterns using prefix, contains, or suffix matching | +| `find-relationship-paths` | Discover all multi-hop relationship chains between two elements via BFS | +| `batch-read-elements` | Read full details for multiple elements in a single request | +| `subgraph-summary` | Summarize descendants of an element with depth, metadata, and relationship counts | +| `element-diff` | Compare two elements and show differences in properties, tags, metadata, and relationships | +| `open-view` | Opens the LikeC4 view panel (editor only) | + +Check out [README](https://github.com/likec4/likec4/blob/main/packages/mcp/README.md) for more details. + +# LikeC4 CLI + +import { PackageManagers } from 'starlight-package-managers'; + +

+![NPM Version](https://img.shields.io/npm/v/likec4) +![NPM Downloads](https://img.shields.io/npm/dm/likec4) +

+ +The `likec4` CLI is a tool for various operations and automation tasks, such as: + +- Start development server to preview diagrams (with hot-reload) +- Build a static website for sharing and embedding diagrams +- Export to PNG, JPEG, JSON, Mermaid, Dot, D2, DrawIO +- Generate [source code artifacts](/tooling/code-generation/react/): + - React components + - Web Components + - Typed data +- Format source files (with CI-friendly check mode) + +## Install + +### Local installation + +If you're using it in an npm project, install it as a development dependency: + + + +You can reference it directly in the `package.json#scripts` object: + +```json5 +{ + scripts: { + dev: 'likec4 dev ...', + build: 'likec4 build ...' + } +} +``` + +### Global installation + +To fetch and execute a package binary, without installing it as a dependency: + + + + +To use it in any project without [`npx`](https://docs.npmjs.com/cli/v11/commands/npx), install it globally: + +```sh +npm install --global likec4 + +# Then, you can call `likec4` directly: +likec4 [command] +``` + +## Usage + +Almost all commands have a `--help` option and provide usage examples. + +```sh +likec4 build -h +likec4 gen react -h +``` + + +### Preview diagrams + +In a folder with LikeC4 sources: + +```sh +likec4 serve +# Aliases: +likec4 start +likec4 dev +``` + +This recursively searches for `*.c4` and `*.likec4` files in the current folder, parses them, and serves diagrams via a local web server. +Any change in the source files triggers a hot update in the browser immediately. + +| Option | Description | +| ------------------------- | --------------------------------------------------------------------------------------------------------- | +| `--port` | Port for the dev server (default: 5173, or `PORT` env var) | +| `--hmr-port` | Port for the HMR WebSocket (default: auto-discovered from 24678–24690, or `HMR_PORT` env var) | +| `--listen, -l` | IP address to listen on (default: `127.0.0.1`, or `0.0.0.0` inside a container) | +| `--no-react-hmr` | Disable React HMR (useful for CI or static previews) | +| `--use-dot` | Use local Graphviz (`dot`) binaries instead of bundled WASM | +| `--public, --public-dir` | Directory whose files are served as-is (Vite [`publicDir`](https://vite.dev/guide/assets#the-public-directory)) | +| `--allowed-host` | Hostname allowed to access the dev server (Vite [`server.allowedHosts`](https://vite.dev/config/server-options#server-allowedhosts)); repeatable. Defaults to allowing all hosts. | + +:::tip +You can start the process in a separate terminal window and keep it running while you're editing the model in your editor, or even serve multiple projects at once. +::: + + + +### Build static website + +CLI can build a website with the diagrams, ready to be deployed or embedded. +When you deployed the website, you can use "Share" button and get a link to a specific diagram. + +The resulting website is strictly bound to the given base path (`/` by default). +A relocatable app can be built with `--base "./"`. + +```sh +likec4 build -o ./dist +``` + +| Option | Description | +| ------------------------ | ----------------------------------------------------------------------------------------------------- | +| `--output` | Output directory | +| `--base, --base-url` | Base URL from which the app is being served, e.g., "/", "/pages/", or "./" for a relocatable app | +| `--use-hash-history` | Hash-based navigation, e.g. "/#/view" instead of "/view" | +| `--use-dot` | Use local binaries of Graphviz ("dot") instead of bundled WASM | +| `--webcomponent-prefix` | Prefix for web components, e.g. "c4" generates `` | +| `--title` | Base title of the app pages (default is "LikeC4") | +| `--output-single-file` | Generates a single self-contained HTML file | +| `--public, --public-dir` | Directory whose files are copied into the output as-is (Vite [`publicDir`](https://vite.dev/guide/assets#the-public-directory)); preserved with `--output-single-file` | + +:::note +Internally, CLI uses Vite to build the website, and `likec4 build` calls `vite build`. +Vite [deploy documentation](https://vitejs.dev/guide/static-deploy.html) may also help you. + +Repository [likec4/template](https://github.com/likec4/template) demonstrates how to deploy likec4 website to github pages. +::: + + +There is also a supplementary command to preview the build: + +```sh +likec4 preview -o ./dist +``` + +For example, this command can be used in CI to compare diagrams with ones from the previous or main build. + +### Export to PNG + +```sh +likec4 export png -o ./assets +likec4 export jpg -o ./assets --quality 90 +``` + +This command starts local web server and uses Playwright to take screenshots. +If you plan to use it in CI, refer to the [Playwright documentation](https://playwright.dev/docs/ci) for details +or consider [LikeC4 GitHub Actions](/tooling/github) + +Use `likec4 export jpg` to export JPEG files. It supports the same screenshot options, plus `--quality`. + +:::note +Exporting to PNG or JPEG requires Playwright. +You will be prompted with a command to install if it's not found. +::: + +| Option | Description | +| ----------------------- | ------------------------------------------------------------------------------------------------------------- | +| `--outdir, -o` | Output directory for PNG/JPEG files; if not specified, images are saved next to sources. `--output` is accepted as alias for compatibility. | +| `--project` | select LikeC4 project to export from by name (e.g. "my-project") or by path
If not specified, exports all | +| `--theme` | color-scheme to use, "light" (default), "dark" | +| `--dark` | use dark theme, shortcut for --theme=dark | +| `--light` | use light theme, shortcut for --theme=light | +| `--use-dot` | Use local binaries of Graphviz ("dot") instead of bundled WASM | +| `--seq, --sequence` | use sequence layout for dynamic views | +| `--flat, --flatten` | Flatten all images in outdir, ignoring source structure | +| `-f, --filter` | include views with ids matching given patterns
(multiple patterns are combined with OR) | +| `--quality, -q` | JPEG quality from 1 to 100 (default is 80); only used by `likec4 export jpg` | +| `-i, --ignore` | continue if export fails for some views | +| `-t, --timeout` | timeout for playwright in seconds, default is 15 | +| `--max-attempts` | max attempts to export failing view, 1 means no retry (default is 3) | +| `--server-url` | use this url instead of starting new likec4 server | +| `--chromium-sandbox` | enable chromium sandbox (see Playwright docs) | +| `--notation` | include view notation in exported PNG/JPEG files | +| `--description` | include the view title and Markdown description in exported PNG/JPEG files | + +### Export to JSON + +```sh +likec4 export json -o dump.json +``` + +### Export to DrawIO + +Export views to DrawIO (`.drawio`) so you can edit diagrams in [draw.io](https://draw.io) or reuse them in tools that support the format. One `.drawio` file is generated per view. + +```sh +likec4 export drawio +likec4 export drawio -o ./diagrams +likec4 export drawio -o ./diagrams src/ +likec4 export drawio --profile leanix -o ./diagrams +``` + +| Option | Description | +| ---------------- | --------------------------------------------------------------------------- | +| `path` | Path to project (default: current directory) | +| `--outdir, -o` | Output directory for `.drawio` files (default: current directory or `path`) | +| `--all-in-one` | Write one `.drawio` file with all views as tabs | +| `--roundtrip` | Apply layout, stroke colors/widths, and edge waypoints from DrawIO round-trip comment blocks in `.c4` source (e.g. after import) | +| `--uncompressed` | Write diagram XML uncompressed (larger file; use if draw.io fails to open the default compressed export) | +| `--profile` | Export profile: `default` (round-trip) or `leanix` (bridge-managed metadata for LeanIX interoperability). Default: `default` | +| `--project` | Select LikeC4 project by name or path; if not set, exports all | +| `--use-dot` | Use local Graphviz (`dot`) for layout instead of bundled WASM | + +:::note +Export to DrawIO requires layout. If Graphviz is not installed, the CLI uses bundled WASM; for large diagrams, install [Graphviz](https://graphviz.org/) and use `--use-dot` for better performance. Use `--roundtrip` when re-exporting after an import so the generated DrawIO matches the previously imported layout and waypoints. Use `--profile leanix` when exporting for LeanIX interoperability; see [Draw.io — Export profiles](/tooling/drawio/#export-profiles). +::: + +### Generate Mermaid, Dot, D2, PlantUml + +Via codegen: + +```sh +likec4 gen mmd +likec4 gen mermaid +likec4 gen dot +likec4 gen d2 +likec4 gen plantuml +``` + +Together with [Export to DrawIO](#export-to-drawio), this allows teams to reuse diagrams in Mermaid, Dot, D2, PlantUML, or DrawIO. + + + + + +### Validate + +```sh +likec4 validate +``` + +This command checks for: +- Syntax errors +- Layout drift (outdated manual layout) + +If any errors are found, the command exits with a non-zero return code. + +### Format + +Format `.c4` source files in place: + +```sh +# Format all files in current workspace +likec4 format +# Alias +likec4 fmt + +# Format a specific workspace directory +likec4 format ./my-project + +# Format only specific project(s) in a multi-project workspace +likec4 format --project alpha --project beta + +# Format specific files +likec4 format --files src/model.c4 --files src/views.c4 + +# Check mode (CI-friendly, exits with code 1 if any file needs formatting) +likec4 format --check +``` + +| Option | Description | +| ------------------- | --------------------------------------------------------------- | +| `path` | Path to workspace (default: current directory; falls back to `LIKEC4_WORKSPACE` env) | +| `--project, -p` | Format only specific project(s) by name (repeatable) | +| `--files` | Format only specific files (repeatable) | +| `--check` | Check if files are formatted without writing changes | + +### Language Server (LSP) + +Start the LikeC4 language server for use with editors that support the Language Server Protocol: + +```sh +likec4 lsp --stdio +likec4 lsp --node-ipc +likec4 lsp --socket 3000 +likec4 lsp --pipe /tmp/likec4.pipe +``` + +One of the transport options (`--stdio`, `--node-ipc`, `--socket`, `--pipe`) is required. + +| Option | Description | +| ------------------- | ----------------------------------------------------------- | +| `--stdio` | Use stdio transport | +| `--node-ipc` | Use node-ipc transport | +| `--socket ` | Use socket transport on specified port | +| `--pipe ` | Use pipe transport with specified pipe name | +| `--watch, -w` | Enable built-in watcher (disabled by default) | +| `--no-manual-layouts` | Disable manual layouts (enabled by default) | +| `--use-dot` | Use local Graphviz (`dot`) for layout instead of bundled WASM | + +# Custom Generators + +import { PackageManagers } from 'starlight-package-managers' + +Ensure you have [`likec4`](https://www.npmjs.com/package/likec4) in your dependencies: + + +
+ +You can define custom generators in your project config: + +```ts +// likec4.config.ts +import { defineConfig } from 'likec4/config' + +export default defineConfig({ + name: 'my-project', + title: 'My Project', + generators: { + 'hello': async ({ likec4model, ctx }) => { + await ctx.write({ + path: 'hello.txt', // relative to the project root + content: `Project: ${likec4model.project.id}`, + }) + }, + }, +}) +``` + +Now you can run your generator with [CLI](/tooling/cli): + +```bash +likec4 gen hello +``` + +In [multi-project](/dsl/config/multi-projects) workspace use: + +```bash +likec4 gen hello --project my-project +# Other options +likec4 gen hello --project my-project --use-dot +``` + +## Reusable Generators + +There is also helper function `defineGenerators` to define reusable generators: + +```ts +// shared_generators.ts +import { defineGenerators } from 'likec4/config' + +export default defineGenerators({ + 'hello': async ({ likec4model, ctx }) => { + await ctx.write({ + path: 'hello.txt', // relative to the project root + content: `Project: ${likec4model.project.id}`, + }) + }, +}) +``` +Now you can use it in your configs: + +```ts +// likec4.config.ts +import { defineConfig } from 'likec4/config' +import generators from './shared_generators' + +export default defineConfig({ + name: 'my-project', + title: 'My Project', + generators, +}) +``` + +:::tip +You can also use smaller package `@likec4/config`: + +```ts +import { defineGenerators } from '@likec4/config' + +export default defineGenerators({ + 'hello': async ({ likec4model, ctx }) => { + await ctx.write({ + path: 'hello.txt', // relative to the project root + content: `Project: ${likec4model.project.id}`, + }) + }, +}) +``` + +::: + +# Generate LikeC4Model + +import { PackageManagers } from 'starlight-package-managers'; + +

+![NPM Version](https://img.shields.io/npm/v/likec4) +![NPM Downloads](https://img.shields.io/npm/dm/likec4) +

+ +Generate source code artifacts from architecture model. + +## Typed Model + + + + + +# Generate React + +import { PackageManagers } from 'starlight-package-managers' + +

+![NPM Version](https://img.shields.io/npm/v/likec4) +![NPM Downloads](https://img.shields.io/npm/dm/likec4) +

+ +Generate React components with views from your architecture model. + +## Install + +Ensure you have [`likec4`](https://www.npmjs.com/package/likec4) in your dependencies: + + +
+ +## React + +The following command generates a JavaScript bundle with React Component (and `.d.ts`): + + + +
+ +```sh frame="none" +# Aliases +npx likec4 generate react -o ./src/likec4.generated.js +npx likec4 gen react -o ./src/likec4.generated.js + +``` + +:::note +Check `likec4 codegen react --help` for available options. +::: + + + + +To use the component: + +```tsx +import { LikeC4View } from './likec4.generated' + +const App = () => { + return ( +
+ +
+ ) +} +``` + +| Property | Description | +| ----------------- | --------------------------------------------------------------------------------------------------- | +| `viewId` | Typed enumeration of your views | +| `where` | Optional, see [filter](#filter) | +| `injectFontCss` | Injects CSS with IBM Plex Sans Variable font from CDN.
Default is `true` | + +:::tip +Check source code for all properties. +::: + +:::caution +`LikeC4View` does not rehydrate correctly if rendered on the server, prefer client-side. +::: + +### Filter + +`where` is same [view predicate](/dsl/views/predicates/#filter), but applies dynamically and enables to show/hide elements based on the context. For example: + +```tsx +import { LikeC4View } from './likec4.generated' + +// Keeps elements and relationships where: +// - tag is not 'legacy' +// - and +// - tag is 'v1' or 'v2' +const App = () => { + return ( +
+ +
+ ) +} +``` + +Layout stays the same, i.e. elements are not rearranged. +Be aware, `where` applies both to elements and relationships. + +## ReactLikeC4 + +`LikeC4View` renders views from your model, and allows exploring in the popup browser. +Component works in most usecases, but if you need more - use `ReactLikeC4`: + +```tsx +import { ReactLikeC4, type LikeC4ViewId } from './likec4.generated' + +const App = () => { + const [viewId, setViewId] = useState('index') + return ( + + ) +} +``` + +`ReactLikeC4` is a low-level component, giving you more control and allowing react to the events. +Check source code for available options. + +Feel free to share your ideas or ask questions in GitHub discussions. + + + +## Styling + + + +# Generate Web Components + +import { PackageManagers } from 'starlight-package-managers' + +

+![NPM Version](https://img.shields.io/npm/v/likec4) +![NPM Downloads](https://img.shields.io/npm/dm/likec4) +

+ +## Install + +Ensure you have [`likec4`](https://www.npmjs.com/package/likec4) in your dependencies: + + +
+ + +## Web Component + +Generate javascript bundle with web component: + + + +
+ +Use it: + +```html + + +``` + +By default, cli generates a `likec4-view` web component. +You can change the `likec4` prefix by `-w, --webcomponent-prefix`. + +For example: + + + +And in HTML: + +```html + +``` + +| Property | Description | +| ----------------- | ----------------------------------------------------------------------------------------------------- | +| `view-id` | Your view id | +| `browser` | Whether to show views browser popup (default `true`) | +| `dynamic-variant` | How dynamic view should be rendered
Possible values: `diagram` or `sequence` (default `diagram`) | +| `color-scheme` | Force light or dark color scheme
Possible values: `light` or `dark` (default: follows system) | + +:::note +CLI command [build](/tooling/cli/#build-static-website) always generates javascript with web components. +Check `Share` button on the top at this example +::: + +# AspireC4 + + +:::caution[Community Project] +AspireC4 is a community-maintained project, not an official LikeC4 package. APIs and behavior may change. +::: + +[AspireC4](https://github.com/kjldev/aspirec4) is a .NET Aspire extension library that auto-generates live LikeC4 architecture diagrams from the Aspire resource graph. Diagrams update in real-time as resources start, stop, or produce errors — and each element links back to the corresponding Aspire dashboard page. + + + + + + + +## Prerequisites + +- **Docker** (required by default) — the LikeC4 server runs as a `ghcr.io/likec4/likec4` container sidecar. +- Alternatively, use `.WithLocalCLI()` to run via a locally-installed Node.js CLI (`npx`, `pnpm`, `yarn`, `bun`, or `deno`). + +## Quick Start + +Add `AspireC4.Hosting` to your AppHost project, then call `AddAspireC4()`: + +```csharp +// AppHost/Program.cs +var builder = DistributedApplication.CreateBuilder(args); + +// Add your services ... +var api = builder.AddProject("my-api"); + +// Register the LikeC4 visualization sidecar. +builder.AddAspireC4(); + +builder.Build().Run(); +``` + +This will: + +1. Write a `./likec4/model.gen.c4` file from the Aspire resource graph. +2. Start a `ghcr.io/likec4/likec4` Docker container serving the live diagram. +3. Watch for resource state changes and regenerate the file automatically. + + + + This simply adds the AspireC4 hosting extension to the AppHost builder. + + ```csharp + // AppHost/Program.cs + var builder = DistributedApplication.CreateBuilder(args); + builder.AddAspireC4(); + + // ...add other projects/resources such as databases, services, etc. + + builder + .AddProject("web") + .WithReference(db); + ``` + + ![Out of the box integration](../../../../assets/tooling/community/aspirec4/makerstack-basic.png) + + + This simply adds the AspireC4 hosting extension to the AppHost builder. + + ```csharp + // AppHost/Program.cs + var builder = DistributedApplication.CreateBuilder(args); + builder.AddAspireC4(opts => { + opts.Title = "MakerStack Architecture"; + }); + + // ...add other projects/resources such as databases, services, etc. + + builder + .AddProject("web") + .WithLikeC4Details(opts => { + opts + .WithLabel("Web Host") + .WithSummary(".NET 10/ ASP.NET Minimal API web app. Hosting APIs, and the Astro-based landing site and the MarkerStack SPA"); + }) + # Note the reference to the database resource (db) here, to save you from calling .WithReference(db) as well... + .WithLikeC4Reference(db, opts => { + opts + .WithLabel("Tabular Data Stream (TDS)") + ... etc + }); + ``` + + ![Enhanced integration](../../../../assets/tooling/community/aspirec4/makerstack-enhanced.png) + + + + +## How It Works + +### Data Flow + +``` +Aspire resources + │ + ▼ +LikeC4ModelBuilder.Build() ← resource states, dashboard URL + │ + ▼ +LikeC4DSLGenerator.Generate() + │ + ▼ +./likec4/model.gen.c4 ← written to disk (and Docker volume) + │ + ▼ +ghcr.io/likec4/likec4 ← serves the diagram, hot-reloads on file change +``` + +### Resource State Colors + +Each Aspire resource is represented as a LikeC4 element. Its color reflects the live runtime state: + +| State | Color | Description | +|-------|-------|-------------| +| Unknown | default | Not yet started | +| Starting | sky | Resource is initializing | +| Running | green | Healthy and running | +| Stopping | slate | Winding down (60% opacity) | +| Exited | muted | Stopped cleanly (30% opacity) | +| Failed | amber | Exited with a non-zero code | +| Error | red | Reported an error state | + +{/* Screenshot placeholder: LikeC4 diagram showing resources in various states */} + +## Dashboard Deep-Links + +When `IncludeAspireDashboardLinks` is enabled (the default), each LikeC4 element receives two links: + +- **Dashboard: Console Logs** → `/consolelogs/resource/{name}` +- **Dashboard: Structured Logs** → `/structuredlogs/resource/{name}` + +Links are built at runtime once the Aspire dashboard reaches the `Running` state. With a browser token (default Aspire setup), the links embed the token for seamless authentication. + +To disable dashboard links: + +```csharp +builder.AddAspireC4(options => +{ + options.IncludeAspireDashboardLinks = false; +}); +``` + + + +## Alternative Modes + +### Local CLI (`WithLocalCLI`) + +Uses a locally-installed `likec4` CLI (via `npx`/`pnpm`/`yarn`/`bun`/`deno`) instead of the Docker container: + +```csharp +builder.AddAspireC4().WithLocalCLI(); +``` + +### Hide from Dashboard (`WithHideFromDashboard`) + +Removes the LikeC4 sidecar from the Aspire dashboard resource list and surfaces the diagram URL as a link and command on each `ProjectResource`: + +```csharp +builder.AddAspireC4().WithHideFromDashboard(); +``` + +## Excluding Resources + +A resource is excluded from the diagram if: + +- It carries an `ExcludeFromLikeC4Annotation`, or +- Its `ResourceSnapshotAnnotation.InitialSnapshot.IsHidden == true` (Aspire internal resources). + +The LikeC4 sidecar resource itself is always excluded. + +## Limitations + +- **Static diagram tool**: LikeC4 renders a file-based diagram. State updates require an HMR refresh. +- **Dashboard URL discovery**: If the Aspire dashboard hasn't started when the first diagram is generated, dashboard links will be absent until it reaches `Running` and triggers a regeneration. +- **Browser token in generated file**: The Aspire browser token appears in the `.c4` file embedded in dashboard link URLs. Do not commit the generated file to source control. +- **Windows HMR relay**: On Windows, a TCP relay bridges the fixed host port `24678` to the dynamically-allocated Docker port for Hot Module Replacement. + +# Erode + + +:::caution[Experimental] +Erode is experimental and in active development. APIs and behavior may change. +::: + +[Erode](https://erode.dev) analyzes code changes against your LikeC4 architecture model using AI, making undeclared dependencies and structural drift visible during code review and while coding. + + + {'https://erode.dev'} + + + + + + + +## Quick Start + +Erode can be integrated in three ways. See the [Erode docs](https://erode.dev/docs/getting-started/) for detailed setup instructions. + +### GitHub Actions + +```yaml +- uses: erode-app/erode@0 +``` + +### CLI + +```shell +npx @erode-app/cli check +``` + +### Claude Code Skill + +Erode is also available as a Claude Code skill — see the [docs](https://erode.dev/docs/getting-started/) for details. + +# MkDocs plugin + + +Colleagues (and our sponsors) from [doubleslash.de](https://doubleslash.de/) made the MkDocs plugin that allows to integrate LikeC4 diagrams +into MkDocs documentation. + + + {'https://github.com/doubleSlashde/mkdocs-likec4'} + + +Check the project's [repository](https://github.com/doubleSlashde/mkdocs-likec4) and documentation website for examples and more details. + + + + + + +## Quick Start + +1. Ensure `likec4` and `graphviz` are available on the build system + +2. Install the `mkdocs-likec4` plugin via `pip`: + ```shell + pip install mkdocs-likec4 + ``` + +3. Add the plugin to your `mkdocs.yml`: + ```yaml + plugins: + - search + - mkdocs-likec4 + ``` + +4. Start embedding views in your markdown: + ````markdown + ```likec4-view + + ``` + ```` + +# LikeC4 Docker + +import { PackageManagers } from 'starlight-package-managers'; + +LikeC4 Docker image is a self-contained environment for running LikeC4 commands and can be used as a drop-in replacement for `likec4` CLI. +It is hosted in [GitHub Container Registry](https://github.com/likec4/likec4/pkgs/container/likec4) and [Docker Hub](https://hub.docker.com/r/likec4/likec4) and includes: + +- Node.js (22.x) +- Graphviz (built from sources of the [latest release](https://gitlab.com/graphviz/graphviz/-/releases)) +- Playwright (latest) +- LikeC4 CLI (latest) + +You can find the [Dockerfile](https://github.com/likec4/likec4/blob/main/Dockerfile) in the repository. + + + + + ```sh copy title="Run any CLI command" + # Example: Help for export command + docker run --rm -t likec4/likec4 export png -h + ``` + + + ```sh copy title="Run any CLI command" + # Example: Help for export command + docker run --rm -t ghcr.io/likec4/likec4 export png -h + ``` + + + +## Usage + +To work with the container you need to mount the folder with LikeC4 sources to `/data` directory +(it is the default working directory, but you can change it and use any other you prefer). + +### Start local web server + +```sh copy title="Start local web server" +# mount LikeC4 sources to /data: -v $(pwd):/data +# publish ports: -p 5173:5173 +# (optional) for realtime updates: -p 24678:24678 +# (optional) use init process to correctly handle signals (eg Ctrl+C): --init +# (optional) enable color output: -t +docker run --rm \ + -v $PWD:/data \ + --init \ + -t \ + -p 5173:5173 \ + -p 24678:24678 \ + -e CHOKIDAR_USEPOLLING=1 \ + -e CHOKIDAR_INTERVAL=200 \ + likec4/likec4 \ + start +``` + +The HMR WebSocket port is auto-discovered from the range 24678–24690. To pin it to a specific port, use `--hmr-port` (or the `HMR_PORT` env var) and publish that exact port: + +```sh copy title="Pin the HMR port" +docker run --rm \ + -v $PWD:/data \ + --init \ + -t \ + -p 5173:5173 \ + -p 24700:24700 \ + -e CHOKIDAR_USEPOLLING=1 \ + -e CHOKIDAR_INTERVAL=200 \ + likec4/likec4 \ + start --hmr-port 24700 +# Or equivalently via env var: +# -e HMR_PORT=24700 +``` + +:::note +By default LikeC4 Docker sets `--use-dot` flag and uses local Graphviz binaries instead of bundled WASM (as it has [memory issues](https://github.com/likec4/likec4/issues?q=Memory%20type:Bug)). + +You can override it with `--no-use-dot` flag. +::: + +:::caution[Running the Local Web Server on Windows] +When using Windows with the Docker-based preview, updates to the diagrams/ file system are not reflected in the running container when files are hosted within a Windows-based file system (i.e. on a drive such as `C:\`). + +This is a [known issue](https://github.com/microsoft/WSL/issues/4739) with file system notifications for Linux applications using WSL. + +**Workaround**: + +Enable polling for file changes by adding the following environment variables to the `docker run` command: + +```diff lang="sh" copy title="Start local web server with WSL workaround" +docker run --rm \ + -v $PWD:/data \ + --init \ + -t \ + -p 5173:5173 \ + -p 24678:24678 \ ++ -e CHOKIDAR_USEPOLLING=1 \ # Enable polling support ++ -e CHOKIDAR_INTERVAL=200 \ # Adjust timing to your needs + likec4/likec4 \ + start +``` + +**Note**: If your diagrams are hosted within a WSL-based filesystem (e.g. /home/user/...), then file system notifications should work as expected. +::: + +### Build static website + +```sh copy title="Build static website" +docker run -v $PWD:/data likec4/likec4 build -o dist +``` + +### Export to PNG + +```sh copy title="Export to PNG" +docker run -v $PWD:/data likec4/likec4 export png --outdir assets --theme dark +``` + +# Draw.io integration + +LikeC4 can **export** views to [Draw.io](https://draw.io) (`.drawio`) so you can edit diagrams in Draw.io or reuse them in tools that support the format. + +**CLI:** `likec4 export drawio` — see [CLI — Export to DrawIO](/tooling/cli/#export-to-drawio). + +This page describes what is mapped on export, limitations, and multi-diagram behavior. + +## Export (LikeC4 → Draw.io) + +By default, one `.drawio` file is produced per view (each view becomes one diagram tab in that file). Use the CLI option `--all-in-one` to combine all views into a single multi-tab `.drawio` file. + +### Export profiles + +Export can use two profiles: + +| Profile | Description | +| --------- | ----------- | +| `default` | Standard round-trip; layout, stroke, waypoints, and existing style attributes (see [Mapped to Draw.io](#mapped-to-drawio) below). | +| `leanix` | Adds **bridge-managed metadata** on vertices, edges, and diagram root so diagrams can be used with the [LeanIX bridge](https://github.com/likec4/likec4/tree/main/packages/leanix-bridge) or LeanIX tooling. LikeC4 remains the source of truth; Draw.io is an interchange format. | + +**When using `leanix` profile:** + +- **Vertices and root cell:** `bridgeManaged=true`, `likec4Id`, `likec4Kind`, `likec4ViewId`, and (if provided) `likec4ProjectId`. Optionally `leanixFactSheetType` per element kind when `leanixFactSheetTypeByKind` is set (e.g. via custom generator). +- **Edges:** `likec4RelationId`, `bridgeManaged`. + +**CLI:** `likec4 export drawio --profile leanix -o ./diagrams` + +**Programmatic:** Pass `profile: 'leanix'` (and optionally `projectId`, `leanixFactSheetTypeByKind`) in `GenerateDrawioOptions` when calling `generateDrawio` / `generateDrawioMulti`. See [@likec4/leanix-bridge](https://github.com/likec4/likec4/tree/main/packages/leanix-bridge) for sync and round-trip mapping. + +### Mapped to Draw.io + +| LikeC4 | Draw.io | +|--------|---------| +| View (view id) | Diagram tab `name` and `id`; view title/description in root cell as `likec4ViewTitle`, `likec4ViewDescription` | +| Element id | mxCell `id` (internal); hierarchy via `parent` | +| Element title, description | mxCell `value` (title; description in HTML or in style `likec4Description`) | +| Element: technology, notes, tags, **navigateTo**, icon | Style `likec4Technology`, `likec4Notes`, `likec4Tags`, `likec4NavigateTo`, `likec4Icon` (URI-encoded). **navigateTo** is also written as a Draw.io cell **link** so the element opens the view tab when clicked (see below). | +| Element: summary, links | Style `likec4Summary`, `likec4Links` (JSON array) | +| Element: notation | Style `likec4Notation` | +| Element: shape | Style `shape` (rectangle, umlActor, cylinder3, document, etc.) | +| Element: color | Style `fillColor`, `strokeColor`, `fontColor` (hex); theme/custom name in `likec4ColorName`; stroke in `likec4StrokeColor` for round-trip | +| Element: border, opacity | Style `strokeWidth`, `likec4Border`, `likec4StrokeWidth` (round-trip); `opacity` | +| Element: size, padding, textSize, iconPosition | Style `likec4Size`, `likec4Padding`, `likec4TextSize`, `likec4IconPosition` | +| View notation | Root cell `likec4ViewNotation` (round-trip) | +| Element position/size | mxGeometry `x`, `y`, `width`, `height` (from layout; overridable via options) | +| Element/edge custom data | mxUserObject `v` (round-trip) | +| Edge waypoints | mxGeometry `relative="0"` with `` of `` (when viewmodel has `points`) | +| Relationship: source, target, label | mxCell edge `source`, `target`, `value` | +| Relationship: description, technology, notes, navigateTo | Style `likec4Description`, `likec4Technology`, `likec4Notes`, `likec4NavigateTo` | +| Relationship: kind, notation | Style `likec4RelationshipKind`, `likec4Notation` | +| Relationship: links, metadata | Style `likec4Links` (JSON array), `likec4Metadata` (JSON object) | +| Relationship: head/tail, line style, color | Style `endArrow`, `startArrow`, `dashed`/`dashPattern`, `strokeColor`; label uses theme `relationships.label` for font color | + +**Navigability (elements with their own view):** If an element has **navigateTo** set (in the model) to a view id, the exported Draw.io cell gets a **link** that opens the diagram tab with id `likec4-`. So when you export all views (`--all-in-one`), clicking that element in the first diagram jumps to the tab of the view that shows the element’s internals. Set `navigateTo ` on the element in your LikeC4 model so the viewmodel provides it; the exporter then adds the link automatically. + +Export uses the **project theme** (or LikeC4 default) for element and relationship colors; containers (bounded contexts) are drawn **behind** their children as a wrapping rectangle; edge **connection points** are derived from layout so arrows attach on the correct sides of nodes. + +The generator accepts optional **options** for round-trip or tooling: `layoutOverride` (per-node bbox: `{ x, y, width, height }`, same shape as `@likec4/core` BBox), `strokeColorByNodeId`, `strokeWidthByNodeId`, `compressed`, and `profile` (default or leanix; when leanix, optionally `projectId`, `leanixFactSheetTypeByKind`). Set **`compressed: false`** to write diagram XML uncompressed inside the `.drawio` file (larger file; use when draw.io desktop or Open file / Import fails to open the default compressed export). The **Playground** exports uncompressed by default for maximum compatibility; the **CLI** uses compressed by default and supports `--uncompressed`. + +### Not preserved on export + +- View rules (include/exclude, style rules) — only the resulting nodes and edges are exported. +- Layout drifts / fine position tweaks — only final node position is written (or the provided `layoutOverride`). +- Theme color names — stored as hex in Draw.io; `likec4ColorName` keeps the name for round-trip. + +## Multi-diagram (multiple tabs) + +When **exporting**, each LikeC4 view is written to a **separate** `.drawio` file (one file per view). With `--all-in-one`, all views are written as tabs in a single file; combined with `--roundtrip`, layout, stroke colors/widths, and edge waypoints from the round-trip comment blocks in `.c4` source are applied per view in that file. + +## Troubleshooting + +**The exported .drawio file does not open in draw.io** (e.g. "Could not add object Array", blank diagram, or draw.io desktop / Open file / Import does nothing): + +- **Playground:** Exports are now **uncompressed** by default so the file opens in draw.io desktop and via Open file / Import. Refresh the app and export again. +- **CLI:** Use **`--uncompressed`** so the diagram XML is written raw (no compression): `likec4 export drawio --uncompressed -o ./out`. This produces a larger file that opens reliably in draw.io desktop. +- If you still use compressed export, **update to the latest release** (older versions used an invalid XML structure for edge waypoints): `pnpm update @likec4/likec4` or `npm update @likec4/likec4`, then export again. + +**The diagram shows fewer elements than expected:** + +- Each view exports **exactly what you see** in that view. A top-level view (e.g. `view index { include * }`) typically shows only **top-level elements** (e.g. Customer and Our SaaS). Nested elements (e.g. Frontend, Backend inside the system) appear in the **element view** of that system (e.g. `view saas of saas { include * }`). To get all elements in Draw.io in one file, use **Export all views** (CLI: `--all-in-one`) and open the tab for the nested view, or switch to that view in the Playground before exporting. + +## Re-export using comment blocks + +When you export to Draw.io from the **Playground**, if the current workspace source contains Draw.io round-trip comment blocks (e.g. `// ` … `// `), the exporter can apply layout, vertex stroke color/width, and edge waypoints from those comments so the generated `.drawio` file matches a previously edited diagram. The CLI supports `--roundtrip` for the same behavior. The generators API exposes `parseDrawioRoundtripComments(source)`, `generateDrawio(viewmodel, options)`, `generateDrawioMulti(viewmodels, optionsByViewId)`, and `GenerateDrawioOptions` (including `edgeWaypoints`, `layoutOverride`, `compressed`) for custom tooling. + +# Editors + + +## VSCode + + + +LikeC4 has official [extension for VSCode](https://marketplace.visualstudio.com/items?itemName=likec4.likec4-vscode) - open-source and available on [GitHub](https://github.com/likec4/likec4/tree/main/packages/vscode). +The extension provides: + +- Validation and error reporting +- Semantic syntax highlighting +- Live Previews (and editing) +- Code completion and navigation +- Resolve references (like `find all references`, `go to definition` .. ) +- "Safe" renames +- Hover information +- [MCP Server](/tooling/ai-tools/#mcp-server) + +Extension is universal and can run in the browser. + +Try [example-cloud-system](https://github.dev/likec4/example-cloud-system) with: + + + + + + + + + +## Standalone Language Server + +For editors other than VSCode, install the standalone language server package: + +```sh +npm install -g @likec4/lsp +``` + +This installs the `likec4-lsp` binary — a self-contained, fully-bundled language server with zero dependencies. +It auto-detects transport from command-line arguments: `--stdio`, `--node-ipc`, `--socket=`, `--pipe=`. + +## Neovim + +LikeC4 has a Neovim plugin for syntax highlighting and LSP integration with code navigation and completion. + +The plugin supports: +- Auto start of the LikeC4 language server for files with .c4 extension +- Validation and error reporting +- Semantic syntax highlighting +- Code completion and navigation +- Resolve references (like `find all references`, `go to definition` .. ) +- "Safe" renames (do not to forget to write your buffers) +- Hover information +- Live Previews (and editing) + +likec4.nvim is available in a separate repository on GitHub - [likec4/likec4.nvim](https://github.com/likec4/likec4.nvim). +Installation: + +```lua +{ + 'likec4/likec4.nvim', + build = 'npm install -g @likec4/lsp' +} +``` + +## Emacs + +There's no MELPA package yet, but you can wire up the [`@likec4/lsp`](#standalone-language-server) standalone language server in a few lines. + +First, define a major wrapper mode (derived from `prog-mode`) so the LSP client knows which buffers to attach to: + +```elisp +;; Major mode for .c4 / .likec4 files +(define-derived-mode likec4-mode prog-mode "LikeC4" + "Major mode for LikeC4 architecture diagram files." + (setq-local comment-start "// ") + (setq-local comment-start-skip "//+ *") + (setq-local comment-end "")) + +(add-to-list 'auto-mode-alist '("\\.c4\\'" . likec4-mode)) +(add-to-list 'auto-mode-alist '("\\.likec4\\'" . likec4-mode)) +``` + +Then pick one of the two LSP clients: + +### Eglot (built-in since Emacs 29) + +```elisp +(use-package eglot + :ensure t + :hook ((likec4-mode . eglot-ensure))) + +(with-eval-after-load 'eglot + (add-to-list 'eglot-server-programs + '(likec4-mode . ("likec4-lsp" "--stdio")))) +``` + +### lsp-mode + +```elisp +(use-package lsp-mode + :ensure t + :commands lsp + :hook ((likec4-mode . lsp)) + :init (setq lsp-keymap-prefix "C-c l") + :config (setq lsp-enable-snippet t)) + +(with-eval-after-load 'lsp-mode + (add-to-list 'lsp-language-id-configuration + '(likec4-mode . "likec4")) + (lsp-register-client + (make-lsp-client + :new-connection (lsp-stdio-connection '("likec4-lsp" "--stdio")) + :activation-fn (lsp-activate-on "likec4") + :server-id 'likec4-lsp))) +``` + +Either approach gives you validation, semantic syntax highlighting, completion, go-to-definition, hover docs, and safe renames — same surface as the [Neovim plugin](#neovim). + +Thanks to [@vincent067](https://github.com/vincent067) for the original setup in [#2268](https://github.com/likec4/likec4/issues/2268). + +## Zed + +LikeC4 has a community Zed extension: [zed-likec4](https://github.com/Lenivvenil/zed-likec4). + +## JetBrains IDEs + +LikeC4 has a JetBrains plugin for syntax highlighting and LSP integration with code navigation and completion. + +The plugin is available in the [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/30490-likec4) and GitHub - [likec4/jetbrains-plugin](https://github.com/likec4/jetbrains-plugin). + +# GitHub Actions + + +![GitHub release](https://img.shields.io/github/release/likec4/actions.svg) + +This action wraps [LikeC4 CLI](/tooling/cli) as a GitHub Action. + + + +### Usage + +#### Build website + +```yaml +steps: + - uses: actions/checkout@v4 + + - name: ⚙️ build + uses: likec4/actions@v1 + with: + action: build + path: src/likec4 + output: dist + base: /baseurl/ + + - name: upload artifacts + uses: actions/upload-artifact@v3 + with: + name: likec4 + path: dist +``` + + +#### Export diagrams to PNG + +```yaml +steps: + - name: export diagrams + uses: likec4/actions@v1 + with: + export: png + path: src/likec4 + output: out/images + use-dot-bin: 'true' +``` + +#### Code generation + +```yaml +steps: + - name: code generation + uses: likec4/actions@v1 + with: + codegen: react + output: __generated__/likec4.jsx +``` + +### Inputs + +| Name | Description | +| ------------- | ----------------------------------------------------------------------------------------------------- | +| `action` | Action to perform (`build` / `export` / `codegen`) | +| `export` | Can be used instead of `action: export` | +| `codegen` | Can be used instead of `action: codegen`, same values as in [cli](https://likec4.dev/docs/tools/cli/) | +| `path` | Path in repository to likec4 sources, root otherwise | +| `output` | Output directory/file | +| `base` | Custom baseUrl for website | +| `use-dot-bin` | if `'true'` will use `dot` binary of graphviz | + +> All inputs are optional. +> By default CLI builds a website to `dist` directory. + +# LikeC4 API + +import { PackageManagers } from 'starlight-package-managers'; + +You can access and traverse your architecture model programmatically using the LikeC4 Model API. + + + +Ensure you have `likec4` in your dependencies: + + + +## Usage + +You can initiate LikeC4 API from a directory with source files or from a string with DSL source. + +### From workspace + +Recursively search and parse source files: + +```ts +import { LikeC4 } from 'likec4' + +const likec4 = await LikeC4.fromWorkspace('/path/to/workspace') +``` + +Method also accepts options: + +| Property | Description | +| -----------------| --------------------------------------------------------------------------------------------------- | +| `printErrors` | if model is invalid, errors are reported to the logger (default `true`) | +| `throwIfInvalid` | return rejected promise if model is invalid (default `false`) | +| `logger` | Whenever to use `default` (console), `vite` logger or your custom implementation
Disable with `false` | +| `graphviz` | `wasm` (default) or `binary` - use local binaries of Graphviz ("dot") or bundled WASM | +| `watch` | Whether to watch for changes in the workspace. (default `false`) | +| `mcp` | Whether to start MCP server.
- `false` - do not start MCP server (default)
- `"stdio"` - use stdio transport,
- `{"port": number}` - use http transport on specified port | + +:::tip +Take a look at [Validate your model](/guides/validate-your-model) for examples of how to enforce custom rules on your model with the LikeC4 API +::: + +### From source + +Parse from the string: + +```ts +import { LikeC4 } from "likec4" + +const likec4 = await LikeC4.fromSource(` + specification { + element system + element user + } + model { + customer = user 'Customer' + cloud = system 'System' + } + views { + view index { + include * + } + } +`) +``` + +### Dispose + +If you initialized LikeC4 with `watch` mode or enabled MCP server, you should dispose it: + +```ts +import { LikeC4 } from 'likec4' + +const likec4 = await LikeC4.fromWorkspace('/path/to/workspace', { + watch: true, + mcp: { port: 33335 }, +}) + +// Cleanup resources +await likec4.dispose() +``` + +LikeC4 is automatically disposed with `await using` declaration: + +```ts +import { LikeC4 } from 'likec4' + +async function() { + await using likec4 = await LikeC4.fromWorkspace('/path/to/workspace', { + watch: true, + mcp: { port: 33335 }, + }) + + // ... + // likec4 is disposed automatically +} +``` + +## API + +When the model is initialized, you can use the following methods to query and traverse it. + +Two types of model (with similar API): + +- **LikeC4Model.Computed** - includes computed views (from predicates), fast, synchronous, enough to traverse but not ready for rendering. +- **LikeC4Model.Layouted** - extends computed model with layout data (dimensions, positions), that is needed for rendering. + +:::tip +Low-level API is available from [`@likec4/core`](https://github.com/likec4/likec4/blob/main/packages/core/README.md). +::: + +### Example + +```ts +import { LikeC4 } from "likec4" + +const likec4 = await LikeC4.fromSource(`....`) + +// Validation errors +console.log(likec4.getErrors()) + +// Traverse the model +const model = likec4.computedModel() + +// Get elements of some kind +const elements = model.elementsOfKind('kind1') + +// Use where operator to filter elements: +// kind is 'kind1' and (tag is 'tag2' or tag is not 'tag3') +const elements = model.elementsWhere({ + and: [ + { kind: 'kind1' }, + { + or: [ + { tag: 'tag2' }, + { + tag: { + neq: 'tag3', + }, + }, + ], + }, + ], +}) + +// Get views that include the element +model + .element('cloud.backend.api') + .views() + +// Get source elements of incoming relationships (filter by tags) +model + .element('cloud.backend.api') + .incoming() // relationships incoming to the element + .filter(r => r.isTagged('http')) // filter by tags + .map(r => r.source) // get source elements + +``` + +To get layouted model: + +```ts +import { LikeC4 } from "likec4" + +const likec4 = await LikeC4.fromSource(`....`) + +const model = await likec4.layoutedModel() + +const diagram = model.view('index') + +// Working with metadata (including array values) +const element = model.element('cloud.backend.api') + +// Get all metadata +const allMetadata = element.getMetadata() +console.log(allMetadata) +// Output: { version: '3.2.1', tags: ['backend', 'gateway'], regions: ['us-east-1', 'eu-west-1'] } + +// Get specific metadata field (could be string or string[]) +const tags = element.getMetadata('tags') +if (Array.isArray(tags)) { + console.log(`Element has ${tags.length} tags: ${tags.join(', ')}`) +} else if (tags) { + console.log(`Element has single tag: ${tags}`) +} + +// Filter elements by array metadata values +const elementsWithBackendTag = model.elements().filter(el => { + const tags = el.getMetadata('tags') + return Array.isArray(tags) + ? tags.includes('backend') + : tags === 'backend' +}) + +``` + +### LikeC4Model + +:::tip +It is possible to generate Typed API from your model, see [Code generation](/tooling/code-generation/model/) +::: + +Model API provides methods to query and traverse the whole model. + +```ts +interface LikeC4Model { + /** + * Returns the root elements of the model. + */ + roots(): Element[]; + /** + * Returns all elements in the model. + */ + elements(): Element[]; + /** + * Returns a specific element by its FQN. + */ + element(id: Fqn): Element; + /** + * Returns all relationships in the model. + */ + relationships(): Relationship[]; + /** + * Returns a specific relationship by its ID. + */ + relationship(id: RelationID): Relationship; + /** + * Returns all views in the model. + */ + views(): ReadonlyArray; + /** + * Returns a specific view by its ID. + */ + view(viewId: ViewID): LikeC4ViewModel; + /** + * Returns the parent element of given element. + * @see ancestors + */ + parent(element: ElementOrFqn): Element | null; + /** + * Get all children of the element (only direct children), + * @see descendants + */ + children(element: ElementOrFqn): Element[]; + /** + * Get all sibling (i.e. same parent) + */ + siblings(element: ElementOrFqn): Element[]; + /** + * Get all ancestor elements (i.e. parent, parent’s parent, etc.) + * (from closest to root) + */ + ancestors(element: ElementOrFqn): Element[]; + /** + * Get all descendant elements (i.e. children, children’s children, etc.) + */ + descendants(element: ElementOrFqn): Element[]; + /** + * Incoming relationships to the element and its descendants + * @see incomers + */ + incoming(element: ElementOrFqn, filter?: 'all' | 'direct' | 'to-descendants'): Relationship[]; + /** + * Source elements of incoming relationships + */ + incomers(element: ElementOrFqn, filter?: 'all' | 'direct' | 'to-descendants'): Element[]; + /** + * Outgoing relationships from the element and its descendants + * @see outgoers + */ + outgoing(element: ElementOrFqn, filter?: 'all' | 'direct' | 'from-descendants'): Relationship[]; + /** + * Target elements of outgoing relationships + */ + outgoers(element: ElementOrFqn, filter?: 'all' | 'direct' | 'from-descendants'): Element[]; +} +``` + +Check sources for methods - [LikeC4Model](https://github.com/likec4/likec4/blob/main/packages/core/src/model/LikeC4Model.ts) + +### Working with Element Metadata + +Elements can have metadata with both single string values and string arrays. The API provides convenient methods to access and work with this metadata: + +```ts +import { LikeC4 } from "likec4" + +const likec4 = await LikeC4.fromSource(` + specification { + element service + element application + } + model { + api = service 'API Gateway' { + metadata { + version '3.2.1' + maintainer 'Platform Team' + tags ['backend', 'gateway', 'microservice'] + regions ['us-east-1', 'eu-west-1'] + critical true + } + } + + frontend = application 'Frontend' { + metadata { + framework 'React' + features ['auth', 'dashboard', 'reports'] + team_members ['alice', 'bob', 'carol'] + release_branch 'main' + } + } + } +`) + +const model = likec4.computedModel() + +// Get element and access metadata +const api = model.element('api') + +// Check if element has any metadata +if (api.hasMetadata()) { + console.log('API has metadata') + + // Get all metadata as an object + const metadata = api.getMetadata() + console.log('All metadata:', metadata) + + // Get specific metadata fields + const version = api.getMetadata('version') // string: '3.2.1' + const tags = api.getMetadata('tags') // string[]: ['backend', 'gateway', 'microservice'] + const regions = api.getMetadata('regions') // string[]: ['us-east-1', 'eu-west-1'] + + // Handle array metadata values + if (Array.isArray(tags)) { + console.log(`API has ${tags.length} tags:`) + tags.forEach(tag => console.log(` - ${tag}`)) + + // Check if specific value exists in array + if (tags.includes('backend')) { + console.log('API is tagged as backend service') + } + } + + // Handle mixed metadata types + const handleMetadataValue = (key: string, value: string | string[] | undefined) => { + if (Array.isArray(value)) { + return `${key}: [${value.join(', ')}]` + } else if (value) { + return `${key}: ${value}` + } + return `${key}: undefined` + } + + console.log(handleMetadataValue('version', version)) + console.log(handleMetadataValue('tags', tags)) + console.log(handleMetadataValue('regions', regions)) +} + +// Advanced filtering using metadata +const backendServices = model.elements() + .filter(element => { + const tags = element.getMetadata('tags') + return Array.isArray(tags) ? tags.includes('backend') : tags === 'backend' + }) + +const multiRegionServices = model.elements() + .filter(element => { + const regions = element.getMetadata('regions') + return Array.isArray(regions) && regions.length > 1 + }) + +// Group elements by metadata values +const elementsByFramework = new Map() +for (const element of model.elements()) { + const framework = element.getMetadata('framework') + if (typeof framework === 'string') { + if (!elementsByFramework.has(framework)) { + elementsByFramework.set(framework, []) + } + elementsByFramework.get(framework)!.push(element) + } +} + +// Collect all unique tags from all elements +const allTags = new Set() +for (const element of model.elements()) { + const tags = element.getMetadata('tags') + if (Array.isArray(tags)) { + tags.forEach(tag => allTags.add(tag)) + } else if (typeof tags === 'string') { + allTags.add(tags) + } +} +console.log('All unique tags:', Array.from(allTags).sort()) +``` + +:::note[Metadata Merging with Extends] +When using [`extend`](/dsl/extend/) to add metadata to elements, duplicate metadata keys are automatically merged: + +- String + String = Array (if different values) +- String + Array = Array (merged) +- Array + Array = Array (merged) +- Duplicate values are automatically de-duplicated +- Single-value arrays are converted back to strings + +This allows you to progressively build up metadata across multiple files. See the [extend documentation](/dsl/extend/#metadata-merging) for more details. +::: + +### LikeC4DeploymentModel + +API provides methods to query and traverse deployment model. + +```ts +import { LikeC4 } from "likec4" + +const likec4 = await LikeC4.fromSource(`....`) +const model = likec4.computedModel() + +// Get deployment model +const deployment = model.deployment + +// Get elements of some kind +for (const instance of deployment.instancesOf('cloud.backend.api')) { + // ... +} +``` + +### LikeC4ViewModel + +View model API provides methods to query and traverse elements and relationships that are included in the view. + +```ts +import { LikeC4 } from "likec4" + +const likec4 = await LikeC4.fromSource(`....`) +const model = likec4.computedModel() + +for (const view of model.views()) { + if (view.isDynamicView()) { + // ... + } +} +``` + + +## Model Builder + +Type-safe builder available from `@likec4/core/builder` (and `likec4/model/builder`). +Builder can be used to create model programmatically and supports two styles: + + + + ```ts + import { Builder } from "@likec4/core/builder" + + const m = Builder + .specification({ + elements: { + actor: { + style: { + shape: 'person', + }, + }, + system: {}, + component: {}, + }, + relationships: { + likes: {}, + }, + tags: ['tag1', 'tag2', 'tag1'], + }) + .model(({ actor, system, component, relTo, rel }, _) => + _( + actor('alice'), + actor('bob'), + rel('alice', 'bob', { + tags: ['tag1'], // you get code completion for tags + kind: 'likes', // code completion for kind + }), + system('cloud', { tags: ['tag1', 'tag2'] }).with( + component('backend').with( + component('api'), + component('db'), + // code completion for relationships + rel('cloud.backend.api', 'cloud.backend.db') + ), + component('frontend').with( + relTo('cloud.backend.api') + ), + ), + ) + ) + .views(({ view, viewOf, $include, $style }, _) => + _( + view('index', 'Index').with( + $include('cloud.*'), + ), + viewOf('ui', 'cloud.ui').with( + // code completion for predicates + $include('* -> cloud.**'), + $style('cloud.ui', { color: 'red' }), + ), + ) + ) + .toLikeC4Model() + ``` + + + + + ```ts + import { Builder } from "@likec4/core/builder" + + // Get composition functions for given specification + const { + model: { + model, + actor, + system, + component, + rel, + relTo, + }, + views: { + view, + viewOf, + views, + $include, + $style, + }, + builder, + } = Builder.forSpecification({ + elements: { + actor: { + style: { + shape: 'person', + }, + }, + system: {}, + component: {}, + }, + relationships: { + likes: {}, + }, + tags: ['tag1', 'tag2', 'tag1'], + }) + + const b1 = builder.with( + model( + actor('alice'), + actor('bob'), + rel('alice', 'bob', { + tags: ['tag1'], + kind: 'likes', + }), + system('cloud', { tags: ['tag1', 'tag2'] }).with( + component('backend').with( + component('api'), + component('db'), + rel('cloud.backend.api', 'cloud.backend.db') + ), + component('frontend').with( + relTo('cloud.backend.api') + ), + ), + ) + ) + + const b2 = b1.with( + views( + view('index', 'Index').with( + $include('cloud.*'), + ), + viewOf('ui', 'cloud.ui').with( + $include('* -> cloud.**'), + $style('cloud.ui', { color: 'red' }), + ), + ) + ) + .toLikeC4Model() + ``` + + + +You can mix both styles, depending on your preference and use cases. + +:::tip +Check unit tests in our repository for examples: +- Builder-style1 +- Builder-style2 +::: + +## Enriching a loaded workspace + +You can load an existing LikeC4 workspace, hand the parsed model back to the [Model Builder](#model-builder), +add elements/relations/views programmatically, and either keep working with the resulting model in memory +or emit DSL source back to disk. + +Typical use cases: + +- Overlay infrastructure pulled from Terraform state, AWS or Kubernetes onto a hand-written model. +- Merge multiple teams' models and add cross-team relationships. +- Generate views by convention (one view per team, per environment, etc.). +- Patch a model from an external service catalogue and re-emit `.c4` source files. + + + +### Reading the parsed model + +`likec4.parsedModel(project?)` returns the parsed-stage model (elements, relations, views, deployments, +globals and imports) *before* view computation: + +```ts +import { LikeC4 } from "likec4" + +const likec4 = await LikeC4.fromWorkspace('./') +const parsed = await likec4.parsedModel() + +// element/relation lookups work the same as on computed models +console.log(parsed.element('cloud.frontend').kind) +console.log(Object.keys(parsed.$data.views)) +``` + +### `LikeC4.toBuilder()` — get a Builder seeded from disk + +`likec4.toBuilder(mode?, project?)` returns a [Model Builder](#model-builder) pre-populated with the +loaded workspace. You can then chain `.model(...)`, `.deployment(...)` and `.views(...)` to add new +content, and call `.toLikeC4Model()` (or `.build()`) to get the enriched model. + +The builder is **editable** by default — re-declaring an element that already exists (same FQN, same +kind) edits it in place rather than throwing, which is what you usually want when patching a loaded +model. Pass `'strict'` to get the strict builder where duplicate FQNs always throw: + +```ts +const editable = await likec4.toBuilder() // editable (default) +const strict = await likec4.toBuilder('strict') // strict +``` + +```ts +import { LikeC4 } from "likec4" + +const likec4 = await LikeC4.fromWorkspace('./') + +const enriched = (await likec4.toBuilder()) + .model(({ system, component }, _) => + _( + system('monitoring').with( + component('grafana'), + component('prometheus'), + ), + ) + ) + .views(({ view, $include }, _) => + _( + view('monitoring', 'Monitoring').with( + $include('monitoring.*'), + ), + ) + ) + .toLikeC4Model() +``` + +:::note +The helper names (`system`, `component`, …) are the **element kinds from the loaded specification** — one +helper per `element ` declared in your DSL. When seeding from disk these are only known at runtime, +so `toBuilder()` returns a loosely-typed builder. To get them statically (with autocomplete and a check +that they match the spec), use [`toTypedBuilder({ specification })`](#likec4totypedbuilder--get-a-typed-builder). +::: + +The same seeding entry is also available directly on the Builder for when you already have the parsed +data in hand: + +```ts +import { Builder } from "@likec4/core/builder" + +const builder = Builder.fromParsed(parsedData) +``` + +### Editable vs strict — editing seeded elements + +`toBuilder()` is editable by default, so you can *edit* seeded entries (for example, to update a title +or merge in new children) just by re-declaring them: + +```ts +import { LikeC4 } from "likec4" + +const likec4 = await LikeC4.fromWorkspace('./') + +const updated = (await likec4.toBuilder()) + .model(({ component }, _) => + _( + component('cloud.api', { title: 'Updated' }), + component('cloud.ui').with( + component('react'), + ), + ) + ) + .toLikeC4Model() +``` + +In editable mode, same-FQN + same-kind redeclaration replaces the element's properties. Redeclaring +with a *different kind* still throws (it's almost certainly a programmer error). If you want +re-declaring any existing FQN to throw, use the strict builder via `await likec4.toBuilder('strict')` +(or `Builder.fromParsed(data, 'strict')`). + +:::caution +Editing **replaces**, it does not **merge**. Re-declaring an element overwrites it with exactly the +properties you pass — any property you omit falls back to its default, it is *not* carried over from the +loaded element. To patch one property while keeping the rest, read the existing element first and spread +the properties you want to preserve (see [Finding and patching existing elements](#finding-and-patching-existing-elements)). +::: + +### Relating new elements to existing ones + +A common task is adding a new element and connecting it to something already in the DSL. Existing FQNs +aren't in the static type set, but you can reference them as plain strings — the relationship is +validated against the seeded model when you build: + +```ts +import { LikeC4 } from "likec4" + +const likec4 = await LikeC4.fromWorkspace('./') + +const enriched = (await likec4.toBuilder()) + .model(({ system, relTo }, _) => + _( + system('monitoring').with( + // `cloud.backend` is an existing element loaded from the DSL + relTo('cloud.backend', 'observes'), + ), + ) + ) + .toLikeC4Model() + +// or, equivalently, from the top level via `rel`: +// .model(({ system, rel }, _) => +// _( +// system('monitoring'), +// rel('monitoring', 'cloud.backend', 'observes'), +// ) +// ) +``` + +Referencing an FQN that doesn't exist throws at build time, so typos surface immediately even though the +compiler can't catch them. + +### Finding and patching existing elements + +To discover what's already in the model, walk it through the [LikeC4Model](#likec4model) returned by +`computedModel()` (or inspect the parsed shape via `parsedModel()`): + +```ts +const model = await likec4.computedModel() +for (const element of model.elements()) { + console.log(element.id, element.kind, element.title) +} +``` + +Because editing **replaces** rather than merges (see the note above), patching a single property means +reading the existing element and passing back the values you want to keep alongside your change: + +```ts +const existing = (await likec4.parsedModel()).element('customer') + +const updated = (await likec4.toBuilder()) + .model(({ actor }, _) => + _( + actor('customer', { + title: existing.title, // keep what you are not changing + metadata: { team: 'platform' }, // add / patch + }), + ) + ) + .toLikeC4Model() +``` + +### `Builder.specification()` — add new kinds and tags + +The instance method `.specification(spec)` extends an existing builder with extra element / deployment / +relationship kinds, tags and metadata keys. Existing keys with the same name are overridden; new keys +are added. Useful when the workspace's specification is a starting point and you want to introduce +overlay-specific kinds: + +```ts +import { LikeC4 } from "likec4" + +const likec4 = await LikeC4.fromWorkspace('./') + +const model = (await likec4.toBuilder()) + .specification({ + elements: { + database: { style: { shape: 'storage' } }, + }, + tags: { + experimental: { color: '#f00' }, + }, + }) + .model(({ database }, _) => + _(database('warehouse', { tags: ['experimental'] })), + ) + .toLikeC4Model() +``` + +The returned builder's types are the **union** of the prior types and the newly-declared spec, so +element-kind helpers, tags and metadata keys accumulate across chained `.specification(...)` calls +instead of being narrowed. + +### `LikeC4.toDSL()` and `writeDSL()` — render DSL back + +`likec4.toDSL(project?)` renders the parsed model back to LikeC4 DSL source. To write that source to +disk, use the Node-only `writeDSL` helper exported from `likec4`: + +```ts +import { LikeC4, writeDSL } from "likec4" + +const likec4 = await LikeC4.fromWorkspace('./') + +// Render to string +const dsl = await likec4.toDSL() + +// Or write to a file (default: ${targetDir}/model.c4) +const outPath = await writeDSL(likec4, './generated') +console.log(`Wrote DSL to ${outPath}`) + +// Custom filename +await writeDSL(likec4, './generated', { fileName: 'snapshot.c4' }) +``` + +You can combine the pieces above to load, enrich, and re-emit in a single pipeline: + +```ts +const enriched = (await likec4.toBuilder()) + .specification({ tags: { autogenerated: { color: '#999' } } }) + .model(({ component }, _) => + _(component('cloud.new-service', { tags: ['autogenerated'] })), + ) + +const data = enriched.build() +const dsl = generateLikeC4(data) // from @likec4/generators/likec4 +await writeFile('./generated/model.c4', dsl) // or use writeDSL on the LikeC4 instance +``` + +### `LikeC4.toTypedBuilder()` — get a *typed* Builder + +When the parsed model comes from a workspace, element kinds and FQNs are only known at runtime — so the +builder returned by `LikeC4.toBuilder()` has loose types (`Builder`). If your code authors the +model with the **same specification** as the DSL, pass that spec to `toTypedBuilder` and get a builder +typed by it — with autocomplete on element/deployment/relationship kinds, tags and metadata keys: + +```ts +const builder = await likec4.toTypedBuilder({ + specification: { + elements: ['actor', 'system', 'component'], + tags: ['external'], + }, +}) + +// `system` / `component` are statically known — no cast needed. +builder.model(({ system, component }, _) => + _(system('monitoring').with(component('grafana'))), +) +``` + +The given specification is **validated** against the loaded model (subset semantics): every kind / tag / +metadata key you declare must exist in the loaded model, otherwise the call throws — so the types can't +silently drift away from the DSL. The loaded model may contain *extra* kinds you didn't declare; you +just won't get typed helpers for them. `mode` defaults to `'editable'`, same as `toBuilder`. + +:::caution +**Existing FQNs stay untyped.** `toTypedBuilder` types the *kinds*, not the FQNs — `findElement('cloud.api')` +and cross-references like `rel('cloud.api', 'cloud.ui')` are still only validated at builder-mutation +time, not by the compiler. + +If you can't (or don't want to) pass a spec, the escape hatch is still an explicit cast on `toBuilder()`: + +```ts +import type { Builder, Types } from "@likec4/core/builder" + +type MySpec = { elements: ['actor', 'system', 'component'] } +const builder = (await likec4.toBuilder()) as unknown as Builder> +``` +::: + +:::tip +For working examples, see +[`Builder.fromParsed.spec.ts`](https://github.com/likec4/likec4/blob/main/packages/core/src/builder/Builder.fromParsed.spec.ts) +and +[`LikeC4.toBuilder.spec.ts`](https://github.com/likec4/likec4/blob/main/packages/language-services/src/__tests__/LikeC4.toBuilder.spec.ts). +::: + +# React Components + +import { PackageManagers } from 'starlight-package-managers' + +

+![NPM Version](https://img.shields.io/npm/v/likec4) +

+ +The LikeC4 React library is available to embed diagrams into your applications. +Although you can use it directly, consider [Vite Plugin](/tooling/vite-plugin/) +or [CLI](/tooling/code-generation/react/) for smoother developer experience. + + + +## Usage + +You must have `react` and `react-dom` installed. +Add [`@likec4/core`](https://www.npmjs.com/package/%40likec4%2Fcore) and [`@likec4/diagram`](https://www.npmjs.com/package/%40likec4%2Fdiagram): + + +
+ +## Keyboard shortcuts + +Focused diagrams support keyboard zoom shortcuts. Click a diagram or tab into it first; while the diagram is focused, +these shortcuts control diagram zoom instead of browser page zoom. Tab out of the diagram to use browser page zoom. + +| Shortcut | Action | +| --- | --- | +| `Ctrl`/`Cmd` + `+` or `=` | Zoom in | +| `Ctrl`/`Cmd` + `-` or `_` | Zoom out | +| `Ctrl`/`Cmd` + `0` | Reset the viewport | + +LikeC4 React library can be used in two ways. + + +### Bundled + +This is the easiest way to use the library. +The diagram renders inside the shadow DOM, includes all dependencies, and handles styling. + +#### LikeC4ModelProvider + +The diagram requires an instance of `LikeC4Model.Layouted` to render. +You need to prepare it and wrap your diagram with the `LikeC4ModelProvider` component. +Below are examples of how to prepare the model: +- Using CLI codegen +- Using Source files +- Using Model Builder + +
+ + + + + Prepare model with [code generation](/tooling/code-generation/model/): + + ```sh + likec4 codegen model --outfile ./likec4-model.ts + ``` + + Then: + + ```tsx copy + import { LikeC4ModelProvider } from '@likec4/diagram/bundle' + // import model from generated file + import { likec4model } from './likec4-model.ts' + + function App() { + return ( + + {/* ... */} + + ) + } + ``` + + + + It's possible to prepare the model from a string. See [API usage](/tooling/model-api/#usage): + ```tsx copy + import { LikeC4 } from 'likec4' + import { LikeC4ModelProvider } from 'likec4/react' + + const likec4 = await LikeC4.fromWorkspace('/path/to/workspace') + const likec4model = await likec4.layoutedModel() + + function App() { + return ( + + {/* ... */} + + ) + } + ``` + + + + You can prepare the model with the [Builder](/tooling/model-api/#model-builder), then lay it out with `layoutLikeC4Model`: + + ```tsx copy collapse={7-52} + import { LikeC4ModelProvider } from '@likec4/diagram/bundle' + import { Builder } from "@likec4/core/builder" + import { layoutLikeC4Model } from "@likec4/layouts" + + const computedModel = Builder + .specification({ + elements: { + actor: { + style: { + shape: 'person', + }, + }, + system: {}, + component: {}, + }, + relationships: { + likes: {}, + }, + tags: ['tag1', 'tag2', 'tag1'], + }) + .model(({ actor, system, component, relTo, rel }, _) => + _( + actor('alice'), + actor('bob'), + rel('alice', 'bob', { + tags: ['tag1'], // you get code completion for tags + kind: 'likes', // code completion for kind + }), + system('cloud', { tags: ['tag1', 'tag2'] }).with( + component('backend').with( + component('api'), + component('db'), + // code completion for relationships + rel('cloud.backend.api', 'cloud.backend.db') + ), + component('frontend').with( + relTo('cloud.backend.api') + ), + ), + ) + ) + .views(({ view, viewOf, $include, $style }, _) => + _( + view('index', 'Index').with( + $include('cloud.*'), + ), + viewOf('ui', 'cloud.ui').with( + $include('* -> cloud.**'), + $style('cloud.ui', { color: 'red' }), + ), + ) + ) + .toLikeC4Model() + + // The Builder returns a computed model. To render it, you need to lay it out + const likec4model = await layoutLikeC4Model(computedModel) + + function App() { + return ( + + {/* ... */} + + ) + } + ``` + + + + + +:::tip +If you have `likec4` as a dependency, you can use: +- `likec4/react` instead of `@likec4/diagram/bundle` +- `likec4/model` instead of `@likec4/core/model` and `@likec4/core/types` +- `likec4/model/builder` instead of `@likec4/core/builder` +- `likec4/icons/all` instead of `@likec4/icons/all` +::: + + +#### LikeC4View + +```tsx +import { LikeC4View, LikeC4ModelProvider } from '@likec4/diagram/bundle' + +function App() { + return ( + + console.log(nodeId)} + /> + {/* Possible to have multiple views */} + + + ) +} +``` + +See [LikeC4ViewProps](https://github.com/likec4/likec4/blob/main/packages/diagram/src/bundle/LikeC4View.props.ts) for available props. + +#### ReactLikeC4 + +`LikeC4View` renders views from your model and allows exploration in the popup browser. +This component works in most use cases, but if you need more functionality, use `ReactLikeC4`: + +```tsx +import { ReactLikeC4, LikeC4ModelProvider } from '@likec4/diagram/bundle' + +function App() { + const [viewId, setViewId] = useState('index') + return ( + + + + ) +} +``` +#### Hooks + +Available hooks inside `LikeC4View` or `ReactLikeC4`: + +```tsx +import { + useLikeC4Model, + useLikeC4Specification, + useLikeC4ViewModel, + useEnabledFeatures, + useCurrentViewId, + + // XYFlow hooks + useXYFlow, + useXYStore, + useXYStoreApi, + + // Diagram API + useDiagram, + + // Select from state + useDiagramContext +} from '@likec4/diagram/bundle' +``` + +#### Icons + +If you use built-in icons, install [`@likec4/icons`](https://www.npmjs.com/package/%40likec4%2Ficons) (or use `likec4/icons`): + +```tsx +import type { ElementIconRenderer } from '@likec4/diagram/bundle' +import { LikeC4ModelProvider, LikeC4View, ReactLikeC4 } from '@likec4/diagram/bundle' +import { lazy, Suspense } from 'react' + +// Better to lazy load icons, bundle is quite large at the moment +const Icon = lazy(async () => { + const { IconRenderer } = await import('@likec4/icons/all') + return { default: IconRenderer } +}) + +const IconRenderer: ElementIconRenderer = (props) => ( + + + +) + +function App() { + return ( + + + {/* Same for ReactLikeC4 */} + + + ) +} +``` + +### Library + +If you want to use the package as a library with your bundler, you need to manage the CSS yourself. + +The library uses [Mantine](https://mantine.dev). If you already use it and have `MantineProvider` in scope, the LikeC4 diagram will use it. +Otherwise, it will wrap itself with `MantineProvider`. +Even if you are not using Mantine in your app, its styles are required for the diagrams to work (don't worry, Mantine is tree-shakable). + +Here are the options: + +#### With bundled styles + +1. Import all styles + + ```css + @import '@likec4/diagram/styles.css' + ``` + + This includes all styles, including the [Mantine](https://mantine.dev) styles. + +2. If you are using Mantine + + ```css + @layer reset, base, mantine, xyflow, tokens, recipes, utilities; + @import "@mantine/core/styles.layer.css"; + @import "@likec4/diagram/styles-min.css"; + ``` + + :::caution + Order of layers is important, make sure `mantine` layer is before `xyflow`, and `xyflow` is before `tokens`. + ::: + +3. Font.\ + LikeC4Diagram uses [`IBM Plex Sans Variable`](https://fontsource.org/fonts/ibm-plex-sans) by default.\ + You can bundle it, import it from [fontsource](https://fontsource.org/fonts/ibm-plex-sans), from any other CDN, or use: + + ```css + @import '@likec4/diagram/styles-font.css' + ``` + + You can override the font, this is explained later. + + +#### With PandaCSS + +Check the [PandaCSS](https://panda-css.com) docs for full setup instructions. +LikeC4 provides a preset. + + +
+ +Configure your `panda.config.ts`: + +```ts +import likec4preset from '@likec4/styles/preset' +import { defineConfig } from '@pandacss/dev' + +export default defineConfig({ + include: [ + 'src/**/*.{ts,tsx}', + // Include likec4 diagram source code to get the styles + './node_modules/@likec4/diagram/panda.buildinfo.json', + ], + importMap: [ + '@likec4/styles', + ], + presets: [ + likec4preset, + ], + theme: { + extend: { + // Here you can override/extend the theme + }, + }, +}) +``` + +You global CSS should look like this: + +```css +@layer reset, base, mantine, xyflow, tokens, recipes, utilities; +@import "@mantine/core/styles.layer.css"; +@import "@likec4/diagram/styles-xyflow.css"; +@import "@likec4/diagram/styles-font.css"; +``` + +#### Usage + +Same as [ReactLikeC4](#reactlikec4), but import from `@likec4/diagram`. +You must provide an instance of `DiagramView`: + +```tsx +import { LikeC4Diagram, LikeC4ModelProvider, useLikeC4ViewModel } from '@likec4/diagram' + +function LikeC4View({viewId}: {viewId: string}) { + const view = useLikeC4ViewModel(viewId) + if (!view) { + return <>View not found + } + return ( + + ) +} + +function App() { + return ( + + + + ) +} +``` + +## Customization + +You can render any component inside `LikeC4Diagram`\ +(or `LikeC4View`/`ReactLikeC4` if you are using bundle): + +```tsx +import { LikeC4Diagram, LikeC4ModelProvider } from '@likec4/diagram' +import { Panel, ViewportPortal } from '@xyflow/react' + +function App() { + return ( + + + + {/* You can use components from xyflow */} + +

Your component as a panel

+ Check examples +
+ + +
+ This div is positioned at [100, 100] on the diagram canvas +
+
+
+ ) +} +``` + +### Custom node renderers + +LikeC4Diagram can use custom node renderers.\ +Compose custom node renderers using primitives from `@likec4/diagram/custom`\ +(or `@likec4/diagram/bundle/custom` for the bundled version).\ +See [customNodes.tsx](https://github.com/likec4/likec4/blob/main/packages/diagram/src/custom/customNodes.tsx) for examples. + +```tsx +import { LikeC4Diagram } from '@likec4/diagram' +import { + ElementActions, + ElementDetailsButtonWithHandler, + elementNode, + ElementNodeContainer, + ElementShape, + ElementTitle, + ElementToolbar, + IfNotReadOnly, +} from '@likec4/diagram/custom' +import { IconPlus } from '@tabler/icons-react' + +const customNodes = { + element: elementNode(({ nodeProps, nodeModel }) => ( + + + + {/* Add extra buttons */} + , + onClick: () => console.log('extra'), + }, + ]} + /> + {/* Add extra info */} +
+ {nodeModel.element.getMetadata('your-attr')} +
+
+ )), +} + +function App() { + return ( + + ) +} +``` + +You can also use [hooks](/tooling/react/#hooks) to access the model and diagram API. + +### Custom styles + +LikeC4Diagram uses [PandaCSS](https://panda-css.com) for styling. You can use it to customize the styles. + +TODO: add example + +# LikeC4 Vite Plugin + +import { PackageManagers } from 'starlight-package-managers' + +

+![NPM Version](https://img.shields.io/npm/v/likec4) +![NPM Downloads](https://img.shields.io/npm/dm/likec4) +

+ +LikeC4 Vite Plugin allows you to embed views directly, without any pre-build/generate steps. +The plugin automatically generates all the necessary code to render the views in your application, with Hot Module Replacement (HMR) supported. + +This is useful for building documentation, tutorials, or any other application where you want to include diagrams. + +## Guide + +
+ + + +1. ### Create Vite project + + To get started, we will need to create a new Vite project using react-ts template. + + +
+
+ +2. ### Install LikeC4 + + Add `likec4` dependency: + + +
+
+ +3. ### Configure Vite + + Add LikeC4 plugin to vite config: + + ```diff lang="ts" + // vite.config.ts + import { defineConfig } from 'vite' + import react from '@vitejs/plugin-react' + + import { LikeC4VitePlugin } from 'likec4/vite-plugin' + + export default defineConfig({ + plugins: [ + react(), + + LikeC4VitePlugin(), + ], + }) + ``` +
+
+ +4. ### Add type references + + Add types reference to the `vite-env.d.ts` file + (or create new one, like `src/likec4.d.ts`) + + ```diff lang="ts" {3} + // src/vite-env.d.ts + /// + /// + ``` + + Another option is to add to the `tsconfig.json`: + + ```json + // tsconfig.json + { + "compilerOptions": { + "types": [ + "likec4/vite-plugin-modules" + ] + } + } + ``` +
+
+ +5. ### Add LikeC4 model + + Create `src/tutorial.c4` and copy the following model from tutorial: + + ```likec4 showLineNumbers copy collapse={14-57} + //src/tutorial.c4 + // Tutorial - https://likec4.dev/tutorial/ + + specification { + element actor + element system + element component + } + + model { + customer = actor 'Customer' { + description 'The regular customer of the system' + } + + saas = system 'Our SaaS' { + component ui 'Frontend' { + description 'Nextjs application, hosted on Vercel' + style { + icon tech:nextjs + shape browser + } + } + component backend 'Backend Services' { + description ' + Implements business logic + and exposes as REST API + ' + } + + // UI requests data from the Backend + ui -> backend 'fetches via HTTPS' + } + + // Customer uses the UI + customer -> ui 'opens in browser' + customer -> saas 'enjoys our product' + } + + views { + + view index { + title 'Landscape view' + + include * + } + + view saas of saas { + include * + + style * { + opacity 25% + } + style customer { + color muted + } + } + + } + ``` +
+
+ +6. ### Use LikeC4 view in your app + + Change the `src/main.tsx` file and import LikeC4 view from the `likec4:react` module: + + ```tsx + // src/main.tsx + import { createRoot } from 'react-dom/client' + import { LikeC4View } from 'likec4:react' + + createRoot(document.getElementById('root')!).render( + + ) + ``` +
+
+ +7. ### Start vite dev server + + + + Open the browser and navigate to `http://localhost:5173/`. + You should see the LikeC4 diagram rendered in your app. + +
+ + + +## Plugin + +### Options + +| Option | Description | +| -----------------| --------------------------------------------------------------------------------------------------- | +| `workspace` | directory with source files (defaults to vite root) | +| `printErrors` | if model is invalid, errors are reported to the logger (default `true`) | +| `throwIfInvalid` | fails with rejected promise if model is invalid (default `false`) | +| `graphviz` | `wasm` (default) or `binary` - use local binaries of Graphviz ("dot") or bundled WASM | + +### Multi-project workspaces + +If you have [multiple projects](/dsl/config/multi-projects/) in your workspace: + +```tsx "project-a" "project-b" +// src/main.tsx + +// where `project-a` and `project-b` are the names of your projects +import { LikeC4View as ProjectA_LikeC4View } from 'likec4:react/project-a' +import { LikeC4View as ProjectB_LikeC4View } from 'likec4:react/project-b' + +const example = () => ( + <> + + + +) +``` +
+ +### Usage with API + +It is also possible to initiate using [LikeC4 API](/tooling/model-api): + +```ts +// vite.config.ts +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import { LikeC4 } from 'likec4' +import { LikeC4VitePlugin } from 'likec4/vite-plugin' + +const { languageServices } = await LikeC4.fromSource(` + specification { + element system + element user + } + model { + customer = user 'Customer' + cloud = system 'System' + } + views { + view index { + include * + } + } +`) + +export default defineConfig({ + plugins: [ + react(), + LikeC4VitePlugin({ + languageServices, + }), + ], +}) +``` +
+ +### Virtual modules + +Other modules are available to get access to the model: + +```tsx +// For multi-project workspaces +import { projects } from 'likec4:projects' + +// Pick first one (default) +import { useLikeC4Views, useLikeC4View } from 'likec4:single-project' + +// Project by name +import { useLikeC4Views, useLikeC4View } from 'likec4:model/project-a' + +// Other modules +import { loadDotSources } from 'likec4:dot' +import { mmdSource } from 'likec4:mmd/project-a' +``` + +Complete list - vite-plugin/modules.d.ts + + +## Usage + +Here are some examples of how to use the plugin with different frameworks + +### With Astro + +You can use LikeC4VitePlugin with Astro and Starlight documentation tool as well. +Configure Astro: + +```js +// astro.config.mjs +import { defineConfig } from 'astro/config'; +import starlight from '@astrojs/starlight'; +import { LikeC4VitePlugin } from 'likec4/vite-plugin' + +export default defineConfig({ + integrations: [ + starlight({ + title: 'Your architecture docs site', + }), + ], + vite: { + plugins: [ + LikeC4VitePlugin({}), + ], + }, +}); +``` + +To use React components, first you need to wrap them in astro components: + +```astro +// src/components/LikeC4View.astro +--- +import { LikeC4View as ReactLikeC4View, type LikeC4ViewId } from 'likec4:react'; +interface Props { + viewId: LikeC4ViewId; +} +const { viewId } = Astro.props +--- + + + +``` + +Then you can use in markdown: + +```mdx +// src/content/docs/example.mdx +--- +title: Welcome to my docs +--- + +import LikeC4View from '../../components/LikeC4View.astro'; + +## Introduction + +This is an example of using LikeC4 in your documentation + + + +``` +
+ +:::tip +Check sources how LikeC4 multi-projects are embedded in this website. +::: + +:::caution +Don't forget to add type references as described [here](/tooling/vite-plugin/#add-type-references). +For Astro this is `src/env.d.ts` file. +::: + +### With Next.js + +For Next.js (since it does not use Vite), there is a workaround - [library mode](https://vite.dev/guide/build.html#library-mode): +Vite will generate a bundled library with likec4 diagrams, that you can import from your Next.js app. + +Export everything from `likec4:react`: + +```tsx +// src/likec4/index.tsx +export * from 'likec4:react' +``` +
+ +Configure Vite: + +```ts +// vite.config.ts +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import { LikeC4VitePlugin } from 'likec4/vite-plugin' + +export default defineConfig({ + build: { + // Build views to 'lib' directory + outDir: 'lib', + lib: { + entry: 'src/likec4/index.tsx', + }, + rollupOptions: { + // make sure to externalize deps that shouldn't be bundled + // to avoid code duplication + external: [ + 'react', + 'react-dom', + 'react/jsx-runtime', + 'react/jsx-dev-runtime', + 'react-dom/client', + ], + }, + }, + plugins: [ + react(), + LikeC4VitePlugin(), + ], +}) +``` + +Run `vite build` and import outputs from your Next.js app: + +```tsx +// pages/index.tsx +import { LikeC4View } from '../lib' + +export default function Home() { + return ( + + ) +} +``` + +You can run `vite build --watch` as a background process to watch for changes in likec4 source files. + +
+
+ +:::tip +You can use LikeC4 with other frameworks as well: +- via [React code generation](/tooling/code-generation/react/) +- For non-react-based documentation tools like Docusaurus, see [Web Components](/tooling/code-generation/webcomponent/) +::: + +# Getting Started + + +To start the tutorial, you have two options: +- Open blank playground in a new tab +- Install [vscode extension](https://marketplace.visualstudio.com/items?itemName=likec4.likec4-vscode) (or open-vsx) and create a new file with `.c4` extension + +and follow the steps: + + + +1. ##### Prepare the specification + + We'll start by defining the kinds of elements in our architecture. + We only need two - `actor` and `system`: + + ```likec4 copy + // tutorial.c4 + specification { + element actor + element system + } + ``` + +2. ##### Create the model + + Start with the top-level elements and define the model: + + ```diff lang="likec4" + // tutorial.c4 + specification { + element actor + element system + } + + + model { + + customer = actor 'Customer' + + saas = system 'Our SaaS' + + } + ``` + + These are the first elements of our architecture model. + Let's add details. + +3. ##### Add a hierarchy + + Assume our system has two main components: `ui` and `backend`. + Let's add a new kind to the specification and update the model. + + ```diff lang="likec4" {5,11-12} copy + // tutorial.c4 + specification { + element actor + element system + + element component + } + + model { + customer = actor 'Customer' + saas = system 'Our SaaS' { + + component ui + + component backend + } + } + ``` + +4. ##### Add relationships + + **Any links** between elements (i.e. interactions, calls, delegations, dependencies, flows). + You are free to define them as you like. + + In the model: + + ```diff lang="likec4" {14-15,18-19} copy + // tutorial.c4 + specification { + element actor + element system + element component + } + + model { + customer = actor 'Customer' + saas = system 'Our SaaS' { + component ui + component backend + + + // UI fetches data from the Backend + + ui -> backend + } + + + // Customer uses the UI + + customer -> ui 'opens in browser' + } + ``` + +5. ##### Create first diagram + + Diagrams are rendered from views. Views are projections of the model defined by predicates (what to include or exclude). + + Start with a bird's eye view (_"Landscape"_): + + ```diff lang="likec4" {23-25} copy + // tutorial.c4 + specification { + element actor + element system + element component + } + + model { + customer = actor 'Customer' + saas = system 'Our SaaS' { + component ui + component backend + + // UI fetches data from the Backend + ui -> backend + + // Customer uses the UI + customer -> ui 'opens in browser' + } + } + + views { + + view index { + + include * + + } + } + ``` + + We got this: + + ![landscape view](../../assets/getting-started/01.png) + + + + +6. ##### Add more views + + ```diff lang="likec4" {27-29} + // tutorial.c4 + specification { + element actor + element system + element component + } + + model { + customer = actor 'Customer' + saas = system 'Our SaaS' { + component ui + component backend + + // UI requests data from the Backend + ui -> backend + + // Customer uses the UI + customer -> ui 'opens in browser' + } + } + + views { + view index { + include * + } + + + view of saas { + + include * + + } + } + ``` + + Imagine we zoom in to the `saas` element and see its nested elements and their relationships: + + ![saas view](../../assets/getting-started/02.png) + +7. ##### Enrich the model + + Let's add descriptions, define the shape of the `ui` and add a label to the relationship `ui -> backend` + + ```diff lang="likec4" {10,15-19,22-25,29,47-49} ins="'fetches via HTTPS'" copy + // tutorial.c4 + specification { + element actor + element system + element component + } + + model { + customer = actor 'Customer' { + + description 'The regular customer of the system' + } + + saas = system 'Our SaaS' { + component ui 'Frontend' { + + description 'Nextjs application, hosted on Vercel' + + style { + + icon tech:nextjs + + shape browser + + } + } + component backend 'Backend Services' { + + description ' + + Implements business logic + + and exposes as REST API + + ' + } + + // UI fetches data from the Backend + ui -> backend 'fetches via HTTPS' + } + + // Customer uses the UI + customer -> ui 'opens in browser' + } + + views { + + view index { + title 'Landscape view' + + include * + } + + view of saas { + include * + + + style customer { + + color muted + + } + } + + } + ``` + + The `saas` view after changes: + + ![saas view after changes](../../assets/getting-started/03.png) + +8. ##### Add changes + + Let's change the description of the `customer` and the label of `customer -> ui` + + ```diff lang="likec4" + // tutorial.c4 + specification { + element actor + element system + element component + } + + model { + customer = actor 'Customer' { + - description 'The regular customer of the system' + + description 'Our dear customer' + } + + saas = system 'Our SaaS' { + component ui 'Frontend' { + description 'Nextjs application, hosted on Vercel' + style { + icon tech:nextjs + shape browser + } + } + component backend 'Backend Services' { + description ' + Implements business logic + and exposes as REST API + ' + } + + // UI requests data from the Backend + ui -> backend 'fetches via HTTPS' + } + + // Customer uses the UI + customer -> ui 'opens in browser' + + customer -> saas 'enjoys our product' + } + + views { + + view index { + title 'Landscape view' + + include * + } + + view of saas { + include * + + style customer { + color muted + } + } + + } + ``` + + View `index`: + + ![landscape view after changes](../../assets/getting-started/04.png) + + View `saas`: + + ![saas view after changes](../../assets/getting-started/05.png) + + :::tip[Did you see?] + We changed elements in the model, and all views are updated accordingly. + ::: + +9. ##### Try it yourself + + Play with [this tutorial in the playground](https://playground.likec4.dev/w/tutorial/) and try adding the following: + + - change [shape](/dsl/styling/#single-element) of the `customer` element + - add a database (with `storage` shape) and tables like `customers` and `orders` (what relationships should be added?) + - add an external system, like Stripe, and show how the backend might interact with it + + + + + diff --git a/.opencode/skills/likec4/references/llms.txt b/.opencode/skills/likec4/references/llms.txt new file mode 100644 index 0000000..4768c19 --- /dev/null +++ b/.opencode/skills/likec4/references/llms.txt @@ -0,0 +1,43 @@ +# LikeC4 Documentation + +- [Project config](https://likec4.dev/dsl/config/) +- [Multi-projects](https://likec4.dev/dsl/config/multi-projects/) +- [TypeScript/JavaScript Config](https://likec4.dev/dsl/config/programmatic/) +- [Deployment Model](https://likec4.dev/dsl/deployment/model/) +- [Deployment views](https://likec4.dev/dsl/deployment/views/) +- [Extending model](https://likec4.dev/dsl/extend/) +- [Introduction](https://likec4.dev/dsl/intro/) +- [Model](https://likec4.dev/dsl/model/) +- [View Notations](https://likec4.dev/dsl/notations/) +- [References](https://likec4.dev/dsl/references/) +- [Relationships](https://likec4.dev/dsl/relationships/) +- [Specification](https://likec4.dev/dsl/specification/) +- [Styling](https://likec4.dev/dsl/styling/) +- [Views](https://likec4.dev/dsl/views/) +- [Generated Views](https://likec4.dev/dsl/views-generated/) +- [Dynamic views](https://likec4.dev/dsl/views/dynamic/) +- [Organize views](https://likec4.dev/dsl/views/organize/) +- [View Predicates](https://likec4.dev/dsl/views/predicates/) +- [Deploy to GitHub Pages](https://likec4.dev/guides/deploy-github-pages/) +- [Embed to website](https://likec4.dev/guides/embed-to-website/) +- [Preview changes in PR](https://likec4.dev/guides/preview-changes-in-pr/) +- [Deploy a static website](https://likec4.dev/guides/static-website/) +- [Enforce and validate your model](https://likec4.dev/guides/validate-your-model/) +- [LikeC4](https://likec4.dev/index/) +- [AI Tools](https://likec4.dev/tooling/ai-tools/) +- [LikeC4 CLI](https://likec4.dev/tooling/cli/) +- [Custom Generators](https://likec4.dev/tooling/code-generation/custom/) +- [Generate LikeC4Model](https://likec4.dev/tooling/code-generation/model/) +- [Generate React](https://likec4.dev/tooling/code-generation/react/) +- [Generate Web Components](https://likec4.dev/tooling/code-generation/webcomponent/) +- [AspireC4](https://likec4.dev/tooling/community/aspirec4/) +- [Erode](https://likec4.dev/tooling/community/erode/) +- [MkDocs plugin](https://likec4.dev/tooling/community/mkdocs-plugin/) +- [LikeC4 Docker](https://likec4.dev/tooling/docker/) +- [Draw.io integration](https://likec4.dev/tooling/drawio/) +- [Editors](https://likec4.dev/tooling/editors/) +- [GitHub Actions](https://likec4.dev/tooling/github/) +- [LikeC4 API](https://likec4.dev/tooling/model-api/) +- [React Components](https://likec4.dev/tooling/react/) +- [LikeC4 Vite Plugin](https://likec4.dev/tooling/vite-plugin/) +- [Getting Started](https://likec4.dev/tutorial/) diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..7d8e054 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,11 @@ +# Project instructions + +## LikeC4 files + +For every task that reads, creates, reviews, or modifies a `*.c4` file: + +- Load the `likec4` skill before proposing or making changes. +- Consult the LikeC4 documentation bundled with that skill. +- Do not infer LikeC4 syntax from similar architecture DSLs. +- Follow `docs/modeling-conventions.md` and keep it synchronized with `src/specification.c4`. +- Run `npm run validate` after changes and resolve any resulting errors. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fa27e13 --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# LikeC4 template + +A small, vendor-neutral starter for describing software architecture with LikeC4. The included fictional Parcel Workshop model demonstrates a system boundary, two services, one relationship kind, and one view without prescribing a real architecture. + +## Prerequisites + +- Node.js 22.22.3 or newer +- npm + +## Setup + +```bash +npm ci +npm start +``` + +The development viewer serves the model locally and reloads when LikeC4 sources change. + +## Scripts + +| Command | Purpose | +| --- | --- | +| `npm start` | Start the local LikeC4 viewer. | +| `npm run dev` | Start the local LikeC4 viewer. | +| `npm run validate` | Validate the model. | +| `npm run format` | Format LikeC4 sources. | +| `npm run format:check` | Check LikeC4 source formatting. | +| `npm run build` | Build the static viewer into `dist/`. | +| `npm run export:drawio` | Export Draw.io files into `generated/drawio/`. | +| `npm run export:png` | Export PNG files into `generated/png/`. | +| `npm run export:json` | Export the model into `generated/model.json`. | + +## Layout + +```text +likec4-template/ +|-- .opencode/skills/likec4/ LikeC4 guidance for OpenCode +|-- docs/modeling-conventions.md +|-- src/specification.c4 Element and relationship kinds +|-- src/model.c4 Architecture elements and relationships +|-- src/views.c4 Diagram views +|-- AGENTS.md Project instructions for OpenCode +|-- likec4.config.json LikeC4 project configuration +|-- opencode.json OpenCode project configuration +|-- package.json npm scripts and LikeC4 dependency +`-- package-lock.json Reproducible npm dependency lock +``` + +## Replace the starter + +1. Replace the fictional elements and relationships in `src/model.c4`. +2. Keep only the kinds used by your model in `src/specification.c4`. +3. Update `src/views.c4` to show the perspectives your readers need. +4. Keep the relationship-kind table in `docs/modeling-conventions.md` synchronized with the specification. +5. Run `npm run format`, `npm run validate`, and `npm run build`. + +LikeC4 merges source files, so a larger model can be split into additional `*.c4` files under `src/` while retaining the specification/model/views separation. + +## Generated output + +Build output is written to `dist/`; exports are written to `generated/`. Both directories are ignored by Git and can be recreated from the model sources. + +## OpenCode support + +`opencode.json` loads the project instructions in `AGENTS.md`. The bundled `.opencode/skills/likec4/` skill supplies LikeC4 documentation and directs model changes to follow `docs/modeling-conventions.md` and pass `npm run validate`. diff --git a/docs/modeling-conventions.md b/docs/modeling-conventions.md new file mode 100644 index 0000000..a82f381 --- /dev/null +++ b/docs/modeling-conventions.md @@ -0,0 +1,48 @@ +# LikeC4 modeling conventions + +These conventions keep a model readable and consistent as it grows. + +## Relationship direction + +Arrows point from the initiating element to the receiving element: + +> initiator -> receiver + +A normal response to a request is implicit. Model a reverse relationship only when it represents an independent architectural interaction. + +Use the most specific useful endpoints. Prefer a parent element when implementation detail would make the view harder to read. + +## Relationship labels + +- Give every relationship a concise, active, present-tense label. +- Prefer precise verbs over the generic `uses`. +- Describe one architectural responsibility per relationship. +- Put protocols and communication technologies in the relationship's `technology` property rather than its title. + +## Relationship kinds + +Use the smallest applicable relationship kind from `src/specification.c4`: + +| Kind | Meaning | +| --- | --- | +| `sends` | Transfers a request or message to another service. | + +The relationship title describes the specific interaction; the kind provides consistent classification. + +```likec4 +intakeDesk .sends routingDesk 'forwards sorting requests' +``` + +Do not introduce a relationship kind for a single exceptional interaction. Add one only when it captures a recurring architectural distinction, and update this table in the same change. + +## Static and dynamic views + +Static views show enduring dependencies and interactions. Use dynamic views when sequence, timing, callbacks, or a multi-step scenario is the subject of the diagram. + +## Visual styling + +Relationship semantics come from direction, kind, and label, not color alone. Keep styling simple unless a visual distinction materially improves a view. + +## Validation + +Run `npm run validate` after every model change. LikeC4 validation checks model correctness, but these project-specific naming and labeling conventions still require review. diff --git a/likec4.config.json b/likec4.config.json new file mode 100644 index 0000000..1645b53 --- /dev/null +++ b/likec4.config.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://likec4.dev/schemas/config.json", + "name": "likec4-template", + "title": "LikeC4 Template", + "exclude": [ + "**/node_modules/**", + "**/dist/**", + "**/generated/**" + ] +} diff --git a/opencode.json b/opencode.json new file mode 100644 index 0000000..6376bc7 --- /dev/null +++ b/opencode.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://opencode.ai/config.json", + "instructions": ["AGENTS.md"] +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..959186f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1943 @@ +{ + "name": "likec4-template", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "likec4-template", + "version": "0.1.0", + "devDependencies": { + "likec4": "1.59.1" + }, + "engines": { + "node": ">=22.22.3" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@hpcc-js/wasm-graphviz": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@hpcc-js/wasm-graphviz/-/wasm-graphviz-1.22.2.tgz", + "integrity": "sha512-qofkC1bxiQKljs95A/7a0j3mvjEdTBiDPq2W6Eh3mJGOLJ+CEtLVe5pFtzf+FZhYW/V9p9hssS1TRl9PxoV8Sw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@likec4/core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@likec4/core/-/core-1.59.1.tgz", + "integrity": "sha512-mkhvPhdoH21iAiyydZDWpLNauBZ1KuRBxz53+kRla58FFP/+gIgPpxH9W+SezKAWfRnlgmDynorzN3Mvn8GgsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immer": "^11.1.9", + "type-fest": "^4.41.0", + "zod": "^4.4.3" + } + }, + "node_modules/@likec4/icons": { + "version": "1.46.4", + "resolved": "https://registry.npmjs.org/@likec4/icons/-/icons-1.46.4.tgz", + "integrity": "sha512-GAL7aW53Mq3RnbFGK8BxHi/vGf73F7ODqgf7yPqfv/Kslt7KxHoxr/ZDIHcSo0oF4tXhpL7GYlpsgY71UJseqw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": "^18.x || ^19.x", + "react-dom": "^18.x || ^19.x" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.146.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.146.0.tgz", + "integrity": "sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.5.tgz", + "integrity": "sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.5.tgz", + "integrity": "sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.5.tgz", + "integrity": "sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.5.tgz", + "integrity": "sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.5.tgz", + "integrity": "sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.5.tgz", + "integrity": "sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.5.tgz", + "integrity": "sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.5.tgz", + "integrity": "sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.5.tgz", + "integrity": "sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.5.tgz", + "integrity": "sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.5.tgz", + "integrity": "sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.5.tgz", + "integrity": "sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.5.tgz", + "integrity": "sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.5.tgz", + "integrity": "sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.5.tgz", + "integrity": "sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.0.tgz", + "integrity": "sha512-qd2BzUBehkov86WFhg0JkEFEYyCLG9uPCe6qWTY/kRlss9OvJrOF2UbIWT7p+8IzZHkEu0DNGHc4HSv+JdDLsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "oxc-transform-react": "^0.145.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "oxc-transform-react": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.0.tgz", + "integrity": "sha512-3oB133prH1o4j/L5lLW7uOCF1PlD+/It2L0eL/iAqWMB91RBbqTewABqxhj0ibBd90EEmWZq7ntIWzVaWcXTGQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/immer": { + "version": "11.1.18", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.18.tgz", + "integrity": "sha512-EQyQtLiYW029lyoczMl/Hh4Xu7cDecSc58JRYpHyL4tIAu3eqd1yJzQX04d2BZHDkzFFvm6qJEJWOtfDSWAXbQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/likec4": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/likec4/-/likec4-1.59.1.tgz", + "integrity": "sha512-uYbh3EnVlhL+LL2eolASHPcePvjduMazI28tzfyDXTSxkXSPT0hv5y05MhEZacoa//RT4cuxFGZyD473kFP+WQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hpcc-js/wasm-graphviz": "1.22.2", + "@likec4/core": "1.59.1", + "@likec4/icons": "1.46.4", + "@vitejs/plugin-react": "^6.0.3", + "bundle-require": "^5.1.0", + "chokidar": "^5.0.0", + "esbuild": "0.28.1", + "fdir": "6.4.0", + "immer": "^11.1.9", + "nano-spawn": "^2.1.0", + "playwright": "1.60.0", + "std-env": "^4.1.0", + "type-fest": "^4.41.0", + "use-sync-external-store": "^1.6.0", + "vite": "^8.1.3", + "vite-plugin-singlefile": "^2.3.3", + "yargs": "17.7.2" + }, + "bin": { + "likec4": "bin/likec4.mjs" + }, + "engines": { + "node": ">=22.22.3" + }, + "peerDependencies": { + "@tanstack/ai": "^0.14.0", + "@tanstack/ai-anthropic": "^0.8.3", + "@tanstack/ai-gemini": "^0.10.0", + "@tanstack/ai-ollama": "^0.6.10", + "@tanstack/ai-openai": "^0.8.2", + "@tanstack/ai-openrouter": "^0.8.2", + "react": "^19.2.x", + "react-dom": "^19.2.x" + }, + "peerDependenciesMeta": { + "@tanstack/ai": { + "optional": true + }, + "@tanstack/ai-anthropic": { + "optional": true + }, + "@tanstack/ai-gemini": { + "optional": true + }, + "@tanstack/ai-ollama": { + "optional": true + }, + "@tanstack/ai-openai": { + "optional": true + }, + "@tanstack/ai-openrouter": { + "optional": true + } + } + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/nano-spawn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.1.0.tgz", + "integrity": "sha512-yTW+2okrElHiH4fsiz/+/zc0EDo9BDDoC3iKk8dpv1GeRc9nUWzUZHx6TofMWErchhUQR8hY9/Eu1Uja9x1nqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" + } + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", + "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", + "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rolldown": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.5.tgz", + "integrity": "sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.146.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.5", + "@rolldown/binding-android-arm64": "1.2.5", + "@rolldown/binding-darwin-arm64": "1.2.5", + "@rolldown/binding-darwin-x64": "1.2.5", + "@rolldown/binding-freebsd-x64": "1.2.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.5", + "@rolldown/binding-linux-arm64-gnu": "1.2.5", + "@rolldown/binding-linux-arm64-musl": "1.2.5", + "@rolldown/binding-linux-ppc64-gnu": "1.2.5", + "@rolldown/binding-linux-s390x-gnu": "1.2.5", + "@rolldown/binding-linux-x64-gnu": "1.2.5", + "@rolldown/binding-linux-x64-musl": "1.2.5", + "@rolldown/binding-openharmony-arm64": "1.2.5", + "@rolldown/binding-win32-arm64-msvc": "1.2.5", + "@rolldown/binding-win32-x64-msvc": "1.2.5" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/vite": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", + "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.26", + "rolldown": "~1.2.4", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0 || ^0.5.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-singlefile": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/vite-plugin-singlefile/-/vite-plugin-singlefile-2.3.3.tgz", + "integrity": "sha512-XVnGH0QzbOa8fxRSsHdCarVN1BSBXNi7uLMQYlrGRN5apdHkk62XQWRJhVever0lnfuyBkwn+kvVChdm/OoOUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">18.0.0" + }, + "peerDependencies": { + "rollup": "^4.59.0", + "vite": "^5.4.21 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5617ea6 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "likec4-template", + "version": "0.1.0", + "private": true, + "description": "A neutral starter project for LikeC4 architecture models", + "engines": { + "node": ">=22.22.3" + }, + "scripts": { + "start": "likec4 serve", + "dev": "likec4 serve", + "validate": "likec4 validate", + "format": "likec4 format", + "format:check": "likec4 format --check", + "build": "likec4 build -o dist", + "export:drawio": "likec4 export drawio --uncompressed -o generated/drawio", + "export:png": "likec4 export png -o generated/png --flat", + "export:json": "likec4 export json -o generated/model.json" + }, + "devDependencies": { + "likec4": "1.59.1" + } +} diff --git a/src/model.c4 b/src/model.c4 new file mode 100644 index 0000000..f9e217e --- /dev/null +++ b/src/model.c4 @@ -0,0 +1,8 @@ +model { + parcelWorkshop = system 'Parcel Workshop' 'Coordinates fictional parcel sorting' { + intakeDesk = service 'Intake Desk' 'Accepts sorting requests' + routingDesk = service 'Routing Desk' 'Plans parcel routes' + + intakeDesk .sends routingDesk 'forwards sorting requests' + } +} diff --git a/src/specification.c4 b/src/specification.c4 new file mode 100644 index 0000000..12f69e6 --- /dev/null +++ b/src/specification.c4 @@ -0,0 +1,23 @@ +specification { + element system { + notation 'System' + + style { + shape rectangle + color primary + } + } + + element service { + notation 'Service' + + style { + shape component + color secondary + } + } + + relationship sends { + notation 'Message transfer' + } +} diff --git a/src/views.c4 b/src/views.c4 new file mode 100644 index 0000000..c327699 --- /dev/null +++ b/src/views.c4 @@ -0,0 +1,8 @@ +views { + view index { + title 'Parcel Workshop Overview' + + include parcelWorkshop + include parcelWorkshop.* + } +}