Bundle Report
+Generated from dist/assets. Use this after builds to spot chunk drift and oversized route payloads quickly.
| Asset | +Type | +Size | +Share | +
|---|
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 17d0a63..15ade00 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -74,9 +74,17 @@ Key area: ## State Management -The app uses a single Zustand store assembled in `src/store.ts`. +The app uses a single public Zustand store exported from `src/store.ts`. -The store is composed from action factories and supported by selective hook files in `src/store/`. +The runtime store is now bootstrapped through: + +- `src/store/createFlowStore.ts` +- `src/store/createFlowStoreState.ts` +- `src/store/createFlowStorePersistOptions.ts` + +This keeps the public entry stable while moving composition, persistence, and hydration concerns behind explicit seams. + +The store is still monolithic at runtime, but it is now partitioned more clearly through slice-typed hooks, selectors, and internal slice factories in `src/store/`. Current store-facing hook files include: @@ -91,6 +99,10 @@ Supporting files: - `defaults.ts` - `types.ts` +- `selectors.ts` +- `slices/createCanvasEditorSlice.ts` +- `slices/createExperienceSlice.ts` +- `slices/createWorkspaceSlice.ts` - `persistence.ts` - `aiSettings.ts` @@ -104,6 +116,7 @@ Persistence is coordinated through: - `src/store/persistence.ts` - `src/services/storage/flowPersistStorage.ts` +- `src/services/storage/storageRuntime.ts` - `src/services/storage/indexedDbStateStorage.ts` Current behavior at a high level: @@ -113,6 +126,10 @@ Current behavior at a high level: - localStorage remains part of the compatibility and fallback story - persisted nodes/edges are sanitized before storage - ephemeral UI fields are excluded from persisted state +- browser storage detection and IndexedDB schema readiness are now funneled through a shared storage runtime helper instead of each storage surface bootstrapping itself independently +- IndexedDB store and index definitions are now declared in one schema manifest in `src/services/storage/indexedDbSchema.ts` +- schema migration markers now live in a dedicated IndexedDB schema metadata store instead of sharing the persisted Zustand state store +- local-first chat persistence now uses document-scoped IndexedDB indexes instead of full chat-message store scans Important constraint: @@ -219,6 +236,12 @@ Examples include: These plugins and registrations allow the app to support multiple structured diagram behaviors without collapsing all logic into the base canvas layer. +Built-in diagram capabilities are now bootstrapped through a shared runtime initialization path instead of scattered one-off registration calls: + +- `src/diagram-types/bootstrap.ts` +- `src/diagram-types/builtInPlugins.ts` +- `src/diagram-types/builtInPropertyPanels.ts` + --- ## Docs Surfaces @@ -244,6 +267,7 @@ Collaboration currently lives under: Current implementation notes: +- collaboration runtime construction now flows through `src/services/collaboration/bootstrap.ts` - realtime transport is built around peer-oriented collaboration - the current stack includes WebRTC-style transport concerns and signaling configuration - fallback behavior exists for unsupported environments diff --git a/README.md b/README.md index 19e9acd..64ca18f 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@
| 🏠 Workspace Home Create · open · import No forced blank file |
-🧑💻 Code → Diagram GitHub · SQL · Terraform K8s · Docker Compose |
+🧑💻 Code → Diagram GitHub · SQL · Terraform K8s · OpenAPI |
🤖 AI Generation 9 providers · BYOK Streaming diff preview |
`{}` Diagram as Code Bidirectional live sync Git-friendly DSL |
🧩 Asset Libraries Developer · AWS · Azure GCP · CNCF · Icons |
@@ -81,7 +81,7 @@ Every diagramming tool makes a compromise. OpenFlowKit doesn't.
| ----------------------- | ----------------------------------------------------------------------------- |
| **Excalidraw / tldraw** | Freeform whiteboards — no structured diagram types, no DSL, no code imports |
| **Mermaid.js** | Code-only — no visual canvas, no AI, no interactive editor |
-| **Draw.io** | Decade-old UX — Limited AI integration, no developer import pipelines |
+| **Draw.io** | Decade-old UX — Limited AI integration, no developer import pipelines |
| **Lucidchart / Miro** | Cloud lock-in — expensive, account required, your data lives on their servers |
| **PlantUML** | Server-dependent rendering — no visual editor, no local-first model |
@@ -91,20 +91,20 @@ OpenFlowKit is the **only MIT-licensed tool** that combines a real workspace hom
## Feature highlights
-| | OpenFlowKit | Excalidraw | Draw.io | Mermaid | Lucidchart |
-| ------------------------------- | :---------: | :--------: | :-----: | :-----: | :--------: |
-| Visual canvas editor | ✅ | ✅ | ✅ | ❌ | ✅ |
-| Bidirectional diagram-as-code | ✅ | ❌ | ❌ | ✅ | ❌ |
-| AI generation (9 providers) | ✅ | ❌ | ❌ | ❌ | Limited |
-| GitHub repo → diagram | ✅ | ❌ | ❌ | ❌ | ❌ |
-| SQL → ERD (native parser) | ✅ | ❌ | ❌ | ❌ | ❌ |
-| Terraform / K8s / Docker import | ✅ | ❌ | ❌ | ❌ | ❌ |
-| AWS / Azure / GCP / CNCF icons | ✅ | ❌ | ✅ | Partial | ✅ |
-| Real-time collaboration (P2P) | ✅ | ✅ | ❌ | ❌ | ✅ (cloud) |
-| Cinematic animated export | ✅ | ❌ | ❌ | ❌ | ❌ |
-| Figma export (editable SVG) | ✅ | ❌ | ❌ | ❌ | ❌ |
-| No account required | ✅ | ✅ | ✅ | ✅ | ❌ |
-| Open source (MIT) | ✅ | ✅ | ✅ | ✅ | ❌ |
+| | OpenFlowKit | Excalidraw | Draw.io | Mermaid | Lucidchart |
+| ------------------------------ | :---------: | :--------: | :-----: | :-----: | :--------: |
+| Visual canvas editor | ✅ | ✅ | ✅ | ❌ | ✅ |
+| Bidirectional diagram-as-code | ✅ | ❌ | ❌ | ✅ | ❌ |
+| AI generation (9 providers) | ✅ | ❌ | ❌ | ❌ | Limited |
+| GitHub repo → diagram | ✅ | ❌ | ❌ | ❌ | ❌ |
+| SQL → ERD (native parser) | ✅ | ❌ | ❌ | ❌ | ❌ |
+| Terraform / K8s import | ✅ | ❌ | ❌ | ❌ | ❌ |
+| AWS / Azure / GCP / CNCF icons | ✅ | ❌ | ✅ | Partial | ✅ |
+| Real-time collaboration (P2P) | ✅ | ✅ | ❌ | ❌ | ✅ (cloud) |
+| Cinematic animated export | ✅ | ❌ | ❌ | ❌ | ❌ |
+| Figma export (editable SVG) | ✅ | ❌ | ❌ | ❌ | ❌ |
+| No account required | ✅ | ✅ | ✅ | ✅ | ❌ |
+| Open source (MIT) | ✅ | ✅ | ✅ | ✅ | ❌ |
---
@@ -125,17 +125,25 @@ CREATE TABLE orders (
→ Typed ERD with inferred foreign-key edges and cardinalities. Rendered in milliseconds, no server involved.
```yaml
-# docker-compose.yml
-services:
- api:
- depends_on: [postgres, redis]
- postgres:
- image: postgres:16
- redis:
- image: redis:alpine
+# deployment.yaml
+apiVersion: apps/v1
+kind: Deployment
+spec:
+ replicas: 3
+---
+apiVersion: v1
+kind: Service
+selector:
+ app: api
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+spec:
+ rules:
+ - host: api.example.com
```
-→ Service architecture with `depends_on` edges and port labels.
+→ Kubernetes architecture with Deployment → Service → Ingress connections.
**AI-powered imports (API key required):**
@@ -143,17 +151,17 @@ services:
github.com/vercel/next.js → architecture diagram
```
-→ Fetches the repo, analyzes code structure and dependencies, then generates an editable architecture diagram via AI. Quality depends on the model chosen.
+→ Fetches a prioritized slice of the GitHub repository in-browser, analyzes the codebase with AI, then generates an editable architecture diagram. Quality depends on repository size, file coverage, and model choice.
| Source | Engine | API key? |
| ------------------------- | -------------------------- | :------: |
-| GitHub repo URL | AI · 9 languages supported | Yes |
+| GitHub repo URL | AI-assisted import | Yes |
| SQL DDL | **Native parser** | **No** |
| Terraform `.tfstate` | **Native parser** | **No** |
| Terraform HCL | AI-assisted | Yes |
| Kubernetes YAML / Helm | **Native parser** | **No** |
-| Docker Compose | **Native parser** | **No** |
-| OpenAPI / Swagger spec | AI-assisted | Yes |
+| OpenAPI / Swagger YAML/JSON | **Native parser** | **No** |
+| OpenAPI source text → richer flow | AI-assisted | Yes |
| Source code (single file) | AI-assisted · 9 languages | Yes |
| Mermaid | **Native parser** | **No** |
@@ -263,14 +271,14 @@ Local-first stays the default. Your saved flows live in the browser, your AI key
## Canvas built for keyboard-first developers
-| Shortcut | Action |
-| ---------------- | ---------------------------------------------------- |
+| Shortcut | Action |
+| ---------------- | --------------------------------------------------------- |
| `⌘ K` / `Ctrl K` | Command bar — search, import, layout, assets, and actions |
-| `⌘ \` / `Ctrl \` | Toggle the live code panel |
-| `⌘ Z` / `Ctrl Z` | Full undo with complete history |
-| `⌘ D` / `Ctrl D` | Duplicate selection |
-| `⌘ G` / `Ctrl G` | Group selected nodes |
-| `⌘ /` / `Ctrl /` | Keyboard shortcuts reference |
+| `⌘ \` / `Ctrl \` | Toggle the live code panel |
+| `⌘ Z` / `Ctrl Z` | Full undo with complete history |
+| `⌘ D` / `Ctrl D` | Duplicate selection |
+| `⌘ G` / `Ctrl G` | Group selected nodes |
+| `⌘ /` / `Ctrl /` | Keyboard shortcuts reference |
Plus: smart alignment guides, snap-to-grid, multi-select, pages, layers, sections, architecture lint, light/dark/system theme, and full i18n in 7 languages.
@@ -314,13 +322,6 @@ npm run build
# upload dist/ to your provider
```
-**Docker:**
-
-```bash
-docker build -t openflowkit .
-docker run -p 8080:80 openflowkit
-```
-
No database. No secrets. No infrastructure. One folder.
---
diff --git a/docs-site/.astro/content.d.ts b/docs-site/.astro/content.d.ts
index f65e91b..29b61b1 100644
--- a/docs-site/.astro/content.d.ts
+++ b/docs-site/.astro/content.d.ts
@@ -169,19 +169,531 @@ declare module 'astro:content' {
>;
type ContentEntryMap = {
-
+ "docs": {
+"ai-generation.md": {
+ id: "ai-generation.md";
+ slug: "ai-generation";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"architecture-lint.md": {
+ id: "architecture-lint.md";
+ slug: "architecture-lint";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"ask-flowpilot.md": {
+ id: "ask-flowpilot.md";
+ slug: "ask-flowpilot";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"aws-architecture.md": {
+ id: "aws-architecture.md";
+ slug: "aws-architecture";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"canvas-basics.md": {
+ id: "canvas-basics.md";
+ slug: "canvas-basics";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"choose-export-format.md": {
+ id: "choose-export-format.md";
+ slug: "choose-export-format";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"choose-input-mode.md": {
+ id: "choose-input-mode.md";
+ slug: "choose-input-mode";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"collaboration-sharing.md": {
+ id: "collaboration-sharing.md";
+ slug: "collaboration-sharing";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"command-center.md": {
+ id: "command-center.md";
+ slug: "command-center";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"context-menu.md": {
+ id: "context-menu.md";
+ slug: "context-menu";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"design-systems-branding.md": {
+ id: "design-systems-branding.md";
+ slug: "design-systems-branding";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"diagram-diff.md": {
+ id: "diagram-diff.md";
+ slug: "diagram-diff";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"diagram-families.md": {
+ id: "diagram-families.md";
+ slug: "diagram-families";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"exporting.md": {
+ id: "exporting.md";
+ slug: "exporting";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"figma-design-import.md": {
+ id: "figma-design-import.md";
+ slug: "figma-design-import";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"github-embed.md": {
+ id: "github-embed.md";
+ slug: "github-embed";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"import-from-data.md": {
+ id: "import-from-data.md";
+ slug: "import-from-data";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"infra-sync.md": {
+ id: "infra-sync.md";
+ slug: "infra-sync";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"introduction.md": {
+ id: "introduction.md";
+ slug: "introduction";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"keyboard-shortcuts.md": {
+ id: "keyboard-shortcuts.md";
+ slug: "keyboard-shortcuts";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"local-first-diagramming.md": {
+ id: "local-first-diagramming.md";
+ slug: "local-first-diagramming";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"mermaid-integration.md": {
+ id: "mermaid-integration.md";
+ slug: "mermaid-integration";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"mermaid-vs-openflow.md": {
+ id: "mermaid-vs-openflow.md";
+ slug: "mermaid-vs-openflow";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"mobile-experience.md": {
+ id: "mobile-experience.md";
+ slug: "mobile-experience";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"node-types.md": {
+ id: "node-types.md";
+ slug: "node-types";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"openflow-dsl.md": {
+ id: "openflow-dsl.md";
+ slug: "openflow-dsl";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"payment-flow.md": {
+ id: "payment-flow.md";
+ slug: "payment-flow";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"playback-history.md": {
+ id: "playback-history.md";
+ slug: "playback-history";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"prompting-agents.md": {
+ id: "prompting-agents.md";
+ slug: "prompting-agents";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"properties-panel.md": {
+ id: "properties-panel.md";
+ slug: "properties-panel";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"quick-start.md": {
+ id: "quick-start.md";
+ slug: "quick-start";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"roadmap.md": {
+ id: "roadmap.md";
+ slug: "roadmap";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"settings.md": {
+ id: "settings.md";
+ slug: "settings";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"smart-layout.md": {
+ id: "smart-layout.md";
+ slug: "smart-layout";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"snapshots-recovery.md": {
+ id: "snapshots-recovery.md";
+ slug: "snapshots-recovery";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"studio-overview.md": {
+ id: "studio-overview.md";
+ slug: "studio-overview";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"templates-assets.md": {
+ id: "templates-assets.md";
+ slug: "templates-assets";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"theming.md": {
+ id: "theming.md";
+ slug: "theming";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/ai-generation.md": {
+ id: "tr/ai-generation.md";
+ slug: "tr/ai-generation";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/architecture-lint.md": {
+ id: "tr/architecture-lint.md";
+ slug: "tr/architecture-lint";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/ask-flowpilot.md": {
+ id: "tr/ask-flowpilot.md";
+ slug: "tr/ask-flowpilot";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/aws-architecture.md": {
+ id: "tr/aws-architecture.md";
+ slug: "tr/aws-architecture";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/canvas-basics.md": {
+ id: "tr/canvas-basics.md";
+ slug: "tr/canvas-basics";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/choose-export-format.md": {
+ id: "tr/choose-export-format.md";
+ slug: "tr/choose-export-format";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/choose-input-mode.md": {
+ id: "tr/choose-input-mode.md";
+ slug: "tr/choose-input-mode";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/collaboration-sharing.md": {
+ id: "tr/collaboration-sharing.md";
+ slug: "tr/collaboration-sharing";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/command-center.md": {
+ id: "tr/command-center.md";
+ slug: "tr/command-center";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/design-systems-branding.md": {
+ id: "tr/design-systems-branding.md";
+ slug: "tr/design-systems-branding";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/diagram-diff.md": {
+ id: "tr/diagram-diff.md";
+ slug: "tr/diagram-diff";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/diagram-families.md": {
+ id: "tr/diagram-families.md";
+ slug: "tr/diagram-families";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/exporting.md": {
+ id: "tr/exporting.md";
+ slug: "tr/exporting";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/figma-design-import.md": {
+ id: "tr/figma-design-import.md";
+ slug: "tr/figma-design-import";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/github-embed.md": {
+ id: "tr/github-embed.md";
+ slug: "tr/github-embed";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/import-from-data.md": {
+ id: "tr/import-from-data.md";
+ slug: "tr/import-from-data";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/infra-sync.md": {
+ id: "tr/infra-sync.md";
+ slug: "tr/infra-sync";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/introduction.md": {
+ id: "tr/introduction.md";
+ slug: "tr/introduction";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/keyboard-shortcuts.md": {
+ id: "tr/keyboard-shortcuts.md";
+ slug: "tr/keyboard-shortcuts";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/local-first-diagramming.md": {
+ id: "tr/local-first-diagramming.md";
+ slug: "tr/local-first-diagramming";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/mermaid-integration.md": {
+ id: "tr/mermaid-integration.md";
+ slug: "tr/mermaid-integration";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/mermaid-vs-openflow.md": {
+ id: "tr/mermaid-vs-openflow.md";
+ slug: "tr/mermaid-vs-openflow";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/node-types.md": {
+ id: "tr/node-types.md";
+ slug: "tr/node-types";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/openflow-dsl.md": {
+ id: "tr/openflow-dsl.md";
+ slug: "tr/openflow-dsl";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/payment-flow.md": {
+ id: "tr/payment-flow.md";
+ slug: "tr/payment-flow";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/playback-history.md": {
+ id: "tr/playback-history.md";
+ slug: "tr/playback-history";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/prompting-agents.md": {
+ id: "tr/prompting-agents.md";
+ slug: "tr/prompting-agents";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/properties-panel.md": {
+ id: "tr/properties-panel.md";
+ slug: "tr/properties-panel";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/quick-start.md": {
+ id: "tr/quick-start.md";
+ slug: "tr/quick-start";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/roadmap.md": {
+ id: "tr/roadmap.md";
+ slug: "tr/roadmap";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/smart-layout.md": {
+ id: "tr/smart-layout.md";
+ slug: "tr/smart-layout";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/studio-overview.md": {
+ id: "tr/studio-overview.md";
+ slug: "tr/studio-overview";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/templates-assets.md": {
+ id: "tr/templates-assets.md";
+ slug: "tr/templates-assets";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/theming.md": {
+ id: "tr/theming.md";
+ slug: "tr/theming";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"tr/v1-beta-launch.md": {
+ id: "tr/v1-beta-launch.md";
+ slug: "tr/v1-beta-launch";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+"v1-beta-launch.md": {
+ id: "v1-beta-launch.md";
+ slug: "v1-beta-launch";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".md"] };
+};
+
};
type DataEntryMap = {
- "docs": Record||||||||
${entry.filename} |
+ ${entry.extension.toUpperCase()} | +${entry.sizeKb} KB | ++ + | +
| Asset | +Type | +Size | +Share | +
|---|
+ {title} +
++ {description} +
++ {fileName} could not be loaded cleanly. Review the issues below, then retry with a corrected file or another export. +
+
+ {issue.snippet}
+
+ ) : null}
+ {issue.hint ? (
+ {issue.hint}
+ ) : null} ++ {remainingIssueCount} more issue{remainingIssueCount === 1 ? '' : 's'} were captured in the latest import report. +
+ ) : null} ++ Lower = more precise and consistent. Higher = more creative and varied. Default: 0.2 +
++ {historyCurrentIndex === 0 + ? t('snapshotsPanel.undoTimelineAtEarliest', 'You are at the earliest captured state.') + : historyCurrentIndex === historyTotalSteps - 1 + ? t('snapshotsPanel.undoTimelineAtLatest', 'You are at the latest state.') + : t('snapshotsPanel.undoTimelinePosition', 'Step {{current}} of {{total}} in the current history stack.', { + current: historyCurrentIndex + 1, + total: historyTotalSteps, + })} +
+{t('snapshotsPanel.noSnapshots')}
diff --git a/src/components/StudioAIPanel.test.tsx b/src/components/StudioAIPanel.test.tsx index f651ba0..bff0226 100644 --- a/src/components/StudioAIPanel.test.tsx +++ b/src/components/StudioAIPanel.test.tsx @@ -4,6 +4,8 @@ import { fireEvent, render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; import { StudioAIPanel } from './StudioAIPanel'; +const handleGenerateMock = vi.fn(); + vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: ( @@ -28,7 +30,7 @@ vi.mock('./command-bar/useAIViewState', () => ({ setSelectedImage: vi.fn(), fileInputRef: createRef- {pendingDiff.previewTitle} -
-- {pendingDiff.previewDetail} -
- ) : null} - {pendingDiff.previewStats && pendingDiff.previewStats.length > 0 ? ( -- {isCanvasEmpty - ? t('commandBar.aiStudio.emptyDescription', { - appName: FLOWPILOT_NAME, - defaultValue: - 'Describe the diagram you want and {{appName}} will draft the first graph for you.', - }) - : t('commandBar.aiStudio.editDescription', { - appName: FLOWPILOT_NAME, - defaultValue: - 'Describe the changes you want and {{appName}} will update the graph for you.', - })} -
- - {aiReadiness.canGenerate && ( -+ {pendingDiff.previewTitle} +
++ {pendingDiff.previewDetail} +
+ ) : null} + {pendingDiff.previewStats && pendingDiff.previewStats.length > 0 ? ( ++ {isCanvasEmpty + ? t('commandBar.aiStudio.emptyDescription', { + appName: FLOWPILOT_NAME, + defaultValue: + 'Describe the diagram you want and {{appName}} will draft the first graph for you.', + }) + : t('commandBar.aiStudio.editDescription', { + appName: FLOWPILOT_NAME, + defaultValue: + 'Describe the changes you want and {{appName}} will update the graph for you.', + })} +
+ + {canGenerate ? ( +{layout.error}
+{limitError}
: null} diff --git a/src/components/command-bar/RootView.tsx b/src/components/command-bar/RootView.tsx index 0a55dbb..14ebef1 100644 --- a/src/components/command-bar/RootView.tsx +++ b/src/components/command-bar/RootView.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useEffect } from 'react'; +import React, { useMemo, useEffect, useId } from 'react'; import { X, ChevronRight } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { FLOWPILOT_NAME } from '@/lib/brand'; @@ -42,12 +42,15 @@ const CommandItemRow = ({ item, isSelected, onClick, + optionId, }: { item: CommandItem; isSelected: boolean; onClick: () => void; + optionId: string; }) => (