forgebook 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +917 -74
- package/dist/bin/forgebook/agentClient.d.ts +2 -1
- package/dist/bin/forgebook/agentClient.js +13 -6
- package/dist/bin/forgebook/agentGuide.d.ts +59 -0
- package/dist/bin/forgebook/agentGuide.js +203 -0
- package/dist/bin/forgebook/agentLoopCommands.mjs +22 -42
- package/dist/bin/forgebook/agentSetup/agentFiles.d.ts +55 -0
- package/dist/bin/forgebook/agentSetup/agentFiles.js +319 -0
- package/dist/bin/forgebook/agentSetup/assistantHooks.d.ts +71 -0
- package/dist/bin/forgebook/agentSetup/assistantHooks.js +125 -0
- package/dist/bin/forgebook/agentSetup/ciWorkflow.d.ts +23 -0
- package/dist/bin/forgebook/agentSetup/ciWorkflow.js +55 -0
- package/dist/bin/forgebook/agentSetup/drawCandidates.d.ts +25 -0
- package/dist/bin/forgebook/agentSetup/drawCandidates.js +116 -0
- package/dist/bin/forgebook/agentSetup/drawDiagnosis.d.ts +28 -0
- package/dist/bin/forgebook/agentSetup/drawDiagnosis.js +134 -0
- package/dist/bin/forgebook/agentSetup/drawRun.d.ts +91 -0
- package/dist/bin/forgebook/agentSetup/drawRun.js +353 -0
- package/dist/bin/forgebook/agentSetup/hookNames.d.ts +7 -0
- package/dist/bin/forgebook/agentSetup/hookNames.js +7 -0
- package/dist/bin/forgebook/agentSetup/initAgentSteps.d.ts +48 -0
- package/dist/bin/forgebook/agentSetup/initAgentSteps.js +104 -0
- package/dist/bin/forgebook/agentSetup/liftedProviders.d.ts +17 -0
- package/dist/bin/forgebook/agentSetup/liftedProviders.js +186 -0
- package/dist/bin/forgebook/agentSetup/projectRunner.d.ts +46 -0
- package/dist/bin/forgebook/agentSetup/projectRunner.js +85 -0
- package/dist/bin/forgebook/agentSetup/stopHook.d.ts +65 -0
- package/dist/bin/forgebook/agentSetup/stopHook.js +202 -0
- package/dist/bin/forgebook/agentSetup/uniformPicture.d.ts +7 -0
- package/dist/bin/forgebook/agentSetup/uniformPicture.js +98 -0
- package/dist/bin/forgebook/agentSetupCommands.d.mts +14 -0
- package/dist/bin/forgebook/agentSetupCommands.mjs +98 -0
- package/dist/bin/forgebook/agentText.d.ts +23 -3
- package/dist/bin/forgebook/agentText.js +175 -28
- package/dist/bin/forgebook/agents.md +20 -191
- package/dist/bin/forgebook/agentsText.js +2 -1
- package/dist/bin/forgebook/appBuild/appBuild.d.ts +59 -0
- package/dist/bin/forgebook/appBuild/appBuild.js +213 -0
- package/dist/bin/forgebook/appBuild/appViteConfig.d.ts +96 -0
- package/dist/bin/forgebook/appBuild/appViteConfig.js +320 -0
- package/dist/bin/forgebook/appBuild/tsconfigPaths.d.ts +14 -0
- package/dist/bin/forgebook/appBuild/tsconfigPaths.js +80 -0
- package/dist/bin/forgebook/auditCommand.d.mts +8 -0
- package/dist/bin/forgebook/auditCommand.mjs +75 -0
- package/dist/bin/forgebook/auditRun.d.ts +95 -0
- package/dist/bin/forgebook/auditRun.js +270 -0
- package/dist/bin/forgebook/browserRun.d.ts +21 -0
- package/dist/bin/forgebook/browserRun.js +57 -0
- package/dist/bin/forgebook/buildFailure.d.ts +7 -0
- package/dist/bin/forgebook/buildFailure.js +47 -0
- package/dist/bin/forgebook/buildWatch.d.ts +52 -0
- package/dist/bin/forgebook/buildWatch.js +137 -0
- package/dist/bin/forgebook/builtCatalogue.js +9 -5
- package/dist/bin/forgebook/catalogueCommands.d.ts +9 -0
- package/dist/bin/forgebook/catalogueCommands.js +23 -0
- package/dist/bin/forgebook/catalogueVite.d.ts +5 -1
- package/dist/bin/forgebook/catalogueVite.js +14 -6
- package/dist/bin/forgebook/commandCacheClaim.d.ts +18 -0
- package/dist/bin/forgebook/commandCacheClaim.js +170 -0
- package/dist/bin/forgebook/commandServer.d.ts +8 -0
- package/dist/bin/forgebook/commandServer.js +61 -13
- package/dist/bin/forgebook/forgebookCommands.mjs +105 -28
- package/dist/bin/forgebook/graphCommand.d.mts +8 -0
- package/dist/bin/forgebook/graphCommand.mjs +84 -0
- package/dist/bin/forgebook/guide/commands.md +57 -0
- package/dist/bin/forgebook/guide/devices.md +17 -0
- package/dist/bin/forgebook/guide/docs-pages.md +13 -0
- package/dist/bin/forgebook/guide/frameworks/angular.md +22 -0
- package/dist/bin/forgebook/guide/frameworks/html.md +22 -0
- package/dist/bin/forgebook/guide/frameworks/nextjs.md +23 -0
- package/dist/bin/forgebook/guide/frameworks/nuxt.md +22 -0
- package/dist/bin/forgebook/guide/frameworks/preact.md +16 -0
- package/dist/bin/forgebook/guide/frameworks/react-native-web.md +21 -0
- package/dist/bin/forgebook/guide/frameworks/react.md +16 -0
- package/dist/bin/forgebook/guide/frameworks/solid.md +18 -0
- package/dist/bin/forgebook/guide/frameworks/svelte.md +18 -0
- package/dist/bin/forgebook/guide/frameworks/sveltekit.md +23 -0
- package/dist/bin/forgebook/guide/frameworks/vue.md +18 -0
- package/dist/bin/forgebook/guide/frameworks/web-components.md +21 -0
- package/dist/bin/forgebook/guide/links.md +29 -0
- package/dist/bin/forgebook/guide/setup.md +23 -0
- package/dist/bin/forgebook/guide/stories.md +90 -0
- package/dist/bin/forgebook/guide/switches.md +43 -0
- package/dist/bin/forgebook/guideCommand.d.mts +13 -0
- package/dist/bin/forgebook/guideCommand.mjs +37 -0
- package/dist/bin/forgebook/importers/storybook.d.ts +21 -0
- package/dist/bin/forgebook/importers/storybook.js +11 -5
- package/dist/bin/forgebook/importers/storybookFrameworks.js +1 -1
- package/dist/bin/forgebook/initCommands.d.mts +11 -0
- package/dist/bin/forgebook/initCommands.mjs +51 -78
- package/dist/bin/forgebook/initConfig.d.ts +5 -3
- package/dist/bin/forgebook/initConfig.js +6 -3
- package/dist/bin/forgebook/initInstall.d.ts +15 -0
- package/dist/bin/forgebook/initInstall.js +77 -0
- package/dist/bin/forgebook/initPrint.d.ts +9 -0
- package/dist/bin/forgebook/initPrint.js +60 -0
- package/dist/bin/forgebook/initRefusals.d.ts +45 -0
- package/dist/bin/forgebook/initRefusals.js +91 -0
- package/dist/bin/forgebook/initRun.d.ts +28 -0
- package/dist/bin/forgebook/initRun.js +311 -0
- package/dist/bin/forgebook/mcpCommand.mjs +38 -4
- package/dist/bin/forgebook/mcpGraph.d.ts +13 -0
- package/dist/bin/forgebook/mcpGraph.js +24 -0
- package/dist/bin/forgebook/mcpTokens.d.ts +14 -0
- package/dist/bin/forgebook/mcpTokens.js +58 -0
- package/dist/bin/forgebook/mcpToolParts.d.ts +7 -0
- package/dist/bin/forgebook/mcpToolParts.js +10 -0
- package/dist/bin/forgebook/mcpTools.d.ts +13 -6
- package/dist/bin/forgebook/mcpTools.js +57 -90
- package/dist/bin/forgebook/mcpVerify.d.ts +16 -0
- package/dist/bin/forgebook/mcpVerify.js +36 -0
- package/dist/bin/forgebook/migrate/codemods.d.ts +11 -0
- package/dist/bin/forgebook/migrate/codemods.js +61 -0
- package/dist/bin/forgebook/migrate/migrate.types.d.ts +117 -0
- package/dist/bin/forgebook/migrate/migrate.types.js +5 -0
- package/dist/bin/forgebook/migrate/migrateCommand.d.mts +8 -0
- package/dist/bin/forgebook/migrate/migrateCommand.mjs +76 -0
- package/dist/bin/forgebook/migrate/migrateRun.d.ts +61 -0
- package/dist/bin/forgebook/migrate/migrateRun.js +210 -0
- package/dist/bin/forgebook/migrate/migrateText.d.ts +12 -0
- package/dist/bin/forgebook/migrate/migrateText.js +30 -0
- package/dist/bin/forgebook/migrate/migrateWrite.d.ts +27 -0
- package/dist/bin/forgebook/migrate/migrateWrite.js +66 -0
- package/dist/bin/forgebook/migrate/objectEdits.d.ts +70 -0
- package/dist/bin/forgebook/migrate/objectEdits.js +241 -0
- package/dist/bin/forgebook/migrate/playOnInBuilds.d.ts +3 -0
- package/dist/bin/forgebook/migrate/playOnInBuilds.js +86 -0
- package/dist/bin/forgebook/migrate/propGroupStrings.d.ts +5 -0
- package/dist/bin/forgebook/migrate/propGroupStrings.js +81 -0
- package/dist/bin/forgebook/newProject/newProject.d.ts +39 -0
- package/dist/bin/forgebook/newProject/newProject.js +180 -0
- package/dist/bin/forgebook/newProject/newProject.types.d.ts +97 -0
- package/dist/bin/forgebook/newProject/newProject.types.js +6 -0
- package/dist/bin/forgebook/newProject/packageManagers.d.ts +51 -0
- package/dist/bin/forgebook/newProject/packageManagers.js +121 -0
- package/dist/bin/forgebook/newProject/projectEntries.d.ts +62 -0
- package/dist/bin/forgebook/newProject/projectEntries.js +191 -0
- package/dist/bin/forgebook/newProject/projectFrameworks.d.ts +57 -0
- package/dist/bin/forgebook/newProject/projectFrameworks.js +148 -0
- package/dist/bin/forgebook/newProject/projectLook.d.ts +23 -0
- package/dist/bin/forgebook/newProject/projectLook.js +293 -0
- package/dist/bin/forgebook/newProject/projectPackages.d.ts +66 -0
- package/dist/bin/forgebook/newProject/projectPackages.js +178 -0
- package/dist/bin/forgebook/newRepair.d.ts +45 -0
- package/dist/bin/forgebook/newRepair.js +157 -0
- package/dist/bin/forgebook/newRepairRun.d.ts +69 -0
- package/dist/bin/forgebook/newRepairRun.js +148 -0
- package/dist/bin/forgebook/newStories.d.ts +23 -1
- package/dist/bin/forgebook/newStories.js +101 -11
- package/dist/bin/forgebook/newStoriesFile.d.ts +20 -1
- package/dist/bin/forgebook/newStoriesFile.js +43 -4
- package/dist/bin/forgebook/prebundleRun.d.ts +3 -1
- package/dist/bin/forgebook/prebundleRun.js +28 -8
- package/dist/bin/forgebook/proofCommand.d.mts +15 -0
- package/dist/bin/forgebook/proofCommand.mjs +212 -0
- package/dist/bin/forgebook/proofRun.d.ts +63 -0
- package/dist/bin/forgebook/proofRun.js +412 -0
- package/dist/bin/forgebook/proofText.d.ts +36 -0
- package/dist/bin/forgebook/proofText.js +131 -0
- package/dist/bin/forgebook/proofWorkflow.d.ts +29 -0
- package/dist/bin/forgebook/proofWorkflow.js +205 -0
- package/dist/bin/forgebook/setArgsTool.d.ts +8 -0
- package/dist/bin/forgebook/setArgsTool.js +70 -0
- package/dist/bin/forgebook/storyCheckCommands.mjs +42 -3
- package/dist/bin/forgebook/storyCheckRun.d.ts +50 -0
- package/dist/bin/forgebook/storyCheckRun.js +83 -9
- package/dist/bin/forgebook/storyFileIds.d.ts +15 -0
- package/dist/bin/forgebook/storyFileIds.js +14 -0
- package/dist/bin/forgebook/stressCases.d.ts +31 -0
- package/dist/bin/forgebook/stressCases.js +79 -0
- package/dist/bin/forgebook/stressRun.d.ts +12 -0
- package/dist/bin/forgebook/stressRun.js +61 -0
- package/dist/bin/forgebook/stressText.d.ts +27 -0
- package/dist/bin/forgebook/stressText.js +46 -0
- package/dist/bin/forgebook/verifyBase.d.ts +45 -0
- package/dist/bin/forgebook/verifyBase.js +196 -0
- package/dist/bin/forgebook/verifyCommand.d.mts +13 -0
- package/dist/bin/forgebook/verifyCommand.mjs +125 -0
- package/dist/bin/forgebook/verifyRun.d.ts +63 -0
- package/dist/bin/forgebook/verifyRun.js +108 -0
- package/dist/bin/forgebook/verifyVerdict.d.ts +40 -0
- package/dist/bin/forgebook/verifyVerdict.js +297 -0
- package/dist/bin/forgebook/workflowActions.d.ts +23 -0
- package/dist/bin/forgebook/workflowActions.js +23 -0
- package/dist/bin/forgebook-docs/catalogueMarkdown.js +12 -1
- package/dist/src/addressArgs.d.ts +54 -6
- package/dist/src/addressArgs.js +72 -16
- package/dist/src/addressArgsParam.d.ts +54 -0
- package/dist/src/addressArgsParam.js +51 -0
- package/dist/src/addressTexts.d.ts +14 -0
- package/dist/src/addressTexts.js +28 -21
- package/dist/src/agentIndexFile.d.ts +11 -0
- package/dist/src/agentIndexFile.js +11 -0
- package/dist/src/argConditions.d.ts +52 -0
- package/dist/src/argConditions.js +80 -0
- package/dist/src/argControl.d.ts +100 -0
- package/dist/src/argControl.js +0 -0
- package/dist/src/argExpression.d.ts +21 -0
- package/dist/src/argExpression.js +30 -0
- package/dist/src/argTypes.d.ts +40 -51
- package/dist/src/argTypes.js +66 -184
- package/dist/src/argsProps.d.ts +3 -3
- package/dist/src/argsProps.js +11 -5
- package/dist/src/bundlerErrors.d.ts +29 -0
- package/dist/src/bundlerErrors.js +49 -0
- package/dist/src/catalogue.d.ts +31 -0
- package/dist/src/catalogue.js +13 -2
- package/dist/src/catalogueRefs.d.ts +75 -0
- package/dist/src/catalogueRefs.js +122 -0
- package/dist/src/componentDocs.d.ts +71 -0
- package/dist/src/componentDocs.js +15 -2
- package/dist/src/frame/callValues.d.ts +19 -0
- package/dist/src/frame/callValues.js +58 -0
- package/dist/src/frame/canvasScheme.d.ts +33 -0
- package/dist/src/frame/canvasScheme.js +67 -0
- package/dist/src/frame/frame.css +36 -3
- package/dist/src/frame/frameStatus.d.ts +16 -1
- package/dist/src/frame/frameStatus.js +16 -1
- package/dist/src/frame/frameView.js +4 -3
- package/dist/src/frame/mountFrame.d.ts +7 -1
- package/dist/src/frame/mountFrame.js +153 -46
- package/dist/src/frame/panelValues.d.ts +35 -0
- package/dist/src/frame/panelValues.js +127 -0
- package/dist/src/frame/playActions.d.ts +39 -0
- package/dist/src/frame/playActions.js +98 -0
- package/dist/src/frame/playStepper.d.ts +28 -0
- package/dist/src/frame/playStepper.js +38 -0
- package/dist/src/frame/playStory.d.ts +9 -1
- package/dist/src/frame/playStory.js +49 -3
- package/dist/src/frame/renderer.d.ts +34 -1
- package/dist/src/frame/renderer.js +24 -0
- package/dist/src/frame/shellChannel.d.ts +1 -1
- package/dist/src/frame/shellChannel.js +1 -1
- package/dist/src/frame/shellCheck.d.ts +17 -0
- package/dist/src/frame/shellCheck.js +25 -0
- package/dist/src/frame/shellMessageCheck.d.ts +7 -0
- package/dist/src/frame/shellMessageCheck.js +53 -0
- package/dist/src/frame/storyContent.d.ts +50 -0
- package/dist/src/frame/storyContent.js +73 -0
- package/dist/src/frame/storyModule.d.ts +9 -0
- package/dist/src/frame/storyModule.js +20 -2
- package/dist/src/frame/storyRun.d.ts +15 -7
- package/dist/src/frame/storyRun.js +64 -15
- package/dist/src/frame/valueSheets.d.ts +35 -0
- package/dist/src/frame/valueSheets.js +99 -0
- package/dist/src/hotModules.d.ts +20 -0
- package/dist/src/hotModules.js +23 -0
- package/dist/src/jsxCode.d.ts +1 -14
- package/dist/src/jsxCode.js +46 -10
- package/dist/src/liveErrors.d.ts +30 -4
- package/dist/src/liveErrors.js +18 -3
- package/dist/src/messages.d.ts +77 -27
- package/dist/src/messages.js +58 -100
- package/dist/src/namedValues.d.ts +2 -26
- package/dist/src/namedValues.js +0 -47
- package/dist/src/pageConfig.d.ts +2 -0
- package/dist/src/playTrace.d.ts +19 -2
- package/dist/src/playTrace.js +10 -5
- package/dist/src/plugin.d.ts +10 -3
- package/dist/src/previewRelay.js +9 -1
- package/dist/src/printable.d.ts +12 -0
- package/dist/src/printable.js +22 -0
- package/dist/src/propsSignature.d.ts +37 -0
- package/dist/src/propsSignature.js +40 -0
- package/dist/src/search.d.ts +41 -5
- package/dist/src/search.js +93 -23
- package/dist/src/searchMatch.d.ts +4 -1
- package/dist/src/searchMatch.js +26 -5
- package/dist/src/searchTypos.d.ts +88 -0
- package/dist/src/searchTypos.js +178 -0
- package/dist/src/shapeValues.d.ts +86 -0
- package/dist/src/shapeValues.js +295 -0
- package/dist/src/shell/ApiReference.js +6 -3
- package/dist/src/shell/ApiTableBox.d.ts +15 -0
- package/dist/src/shell/ApiTableBox.js +11 -0
- package/dist/src/shell/AppearanceMenu.d.ts +4 -1
- package/dist/src/shell/AppearanceMenu.js +9 -6
- package/dist/src/shell/CodeTab.d.ts +3 -2
- package/dist/src/shell/CodeTab.js +3 -2
- package/dist/src/shell/CodeText.d.ts +1 -1
- package/dist/src/shell/CodeText.js +2 -3
- package/dist/src/shell/CompareGrid.js +1 -1
- package/dist/src/shell/ComponentPage.d.ts +1 -1
- package/dist/src/shell/ComponentPage.js +5 -1
- package/dist/src/shell/CustomControl.d.ts +34 -0
- package/dist/src/shell/CustomControl.js +132 -0
- package/dist/src/shell/DocContainers.js +1 -1
- package/dist/src/shell/DocPage.js +2 -2
- package/dist/src/shell/DroppedArgsList.d.ts +2 -2
- package/dist/src/shell/DroppedArgsList.js +1 -1
- package/dist/src/shell/EmbedPage.js +58 -14
- package/dist/src/shell/Example.d.ts +5 -1
- package/dist/src/shell/Example.js +16 -8
- package/dist/src/shell/FrameView.d.ts +6 -3
- package/dist/src/shell/FrameView.js +2 -2
- package/dist/src/shell/HomePage.d.ts +3 -1
- package/dist/src/shell/HomePage.js +16 -6
- package/dist/src/shell/InfoTip.js +2 -2
- package/dist/src/shell/InteractionsTab.d.ts +11 -4
- package/dist/src/shell/InteractionsTab.js +18 -2
- package/dist/src/shell/LazyPage.js +1 -1
- package/dist/src/shell/PackageHtml.js +3 -1
- package/dist/src/shell/PinnedControl.js +14 -62
- package/dist/src/shell/PinnedControlMenu.d.ts +44 -0
- package/dist/src/shell/PinnedControlMenu.js +72 -0
- package/dist/src/shell/PluginBlockHost.js +2 -1
- package/dist/src/shell/PluginTabHost.js +19 -2
- package/dist/src/shell/PropFields.d.ts +3 -1
- package/dist/src/shell/PropFields.js +9 -6
- package/dist/src/shell/PropRows.d.ts +23 -2
- package/dist/src/shell/PropRows.js +58 -19
- package/dist/src/shell/PropsTab.d.ts +12 -5
- package/dist/src/shell/PropsTab.js +9 -21
- package/dist/src/shell/RefStoryPage.d.ts +33 -0
- package/dist/src/shell/RefStoryPage.js +123 -0
- package/dist/src/shell/SearchPanel.js +51 -24
- package/dist/src/shell/Shell.d.ts +18 -0
- package/dist/src/shell/Shell.js +137 -22
- package/dist/src/shell/ShortcutHelp.d.ts +22 -0
- package/dist/src/shell/ShortcutHelp.js +66 -0
- package/dist/src/shell/Sidebar.d.ts +17 -4
- package/dist/src/shell/Sidebar.js +92 -41
- package/dist/src/shell/SidebarDocs.js +2 -2
- package/dist/src/shell/SidebarEntries.js +5 -3
- package/dist/src/shell/SidebarHandle.d.ts +3 -5
- package/dist/src/shell/SidebarHandle.js +4 -4
- package/dist/src/shell/SidebarRefs.d.ts +39 -0
- package/dist/src/shell/SidebarRefs.js +99 -0
- package/dist/src/shell/SidebarSearch.js +37 -9
- package/dist/src/shell/SignatureReference.js +3 -1
- package/dist/src/shell/Stage.d.ts +10 -2
- package/dist/src/shell/Stage.js +35 -60
- package/dist/src/shell/StatusBadges.d.ts +54 -0
- package/dist/src/shell/StatusBadges.js +77 -0
- package/dist/src/shell/StoryCodeBlock.d.ts +8 -19
- package/dist/src/shell/StoryCodeBlock.js +7 -6
- package/dist/src/shell/SwitchGallery.d.ts +36 -0
- package/dist/src/shell/SwitchGallery.js +17 -0
- package/dist/src/shell/SwitchGalleryPanel.d.ts +37 -0
- package/dist/src/shell/SwitchGalleryPanel.js +99 -0
- package/dist/src/shell/TagFilter.d.ts +24 -0
- package/dist/src/shell/TagFilter.js +45 -0
- package/dist/src/shell/TagViews.d.ts +39 -0
- package/dist/src/shell/TagViews.js +20 -0
- package/dist/src/shell/Toolbar.d.ts +4 -1
- package/dist/src/shell/Toolbar.js +17 -7
- package/dist/src/shell/ToolbarControl.d.ts +3 -0
- package/dist/src/shell/ToolbarControl.js +23 -6
- package/dist/src/shell/ViewMenuPanel.js +6 -1
- package/dist/src/shell/argChecks.d.ts +22 -0
- package/dist/src/shell/argChecks.js +20 -0
- package/dist/src/shell/asideStory.d.ts +17 -4
- package/dist/src/shell/asideStory.js +21 -4
- package/dist/src/shell/audienceSections.css +9 -9
- package/dist/src/shell/catalogueContext.d.ts +1 -1
- package/dist/src/shell/catalogueContext.js +6 -1
- package/dist/src/shell/codeSpans.d.ts +27 -0
- package/dist/src/shell/codeSpans.js +76 -0
- package/dist/src/shell/compareVariants.d.ts +4 -2
- package/dist/src/shell/compareVariants.js +16 -8
- package/dist/src/shell/composition.d.ts +23 -0
- package/dist/src/shell/composition.js +26 -0
- package/dist/src/shell/controlCycle.d.ts +1 -1
- package/dist/src/shell/controlCycle.js +3 -2
- package/dist/src/shell/customControls.d.ts +55 -0
- package/dist/src/shell/customControls.js +70 -0
- package/dist/src/shell/docPage.css +22 -22
- package/dist/src/shell/dockTabs.d.ts +19 -3
- package/dist/src/shell/dockTabs.js +28 -20
- package/dist/src/shell/embed.css +7 -7
- package/dist/src/shell/framesOrigin.d.ts +7 -0
- package/dist/src/shell/framesOrigin.js +1 -1
- package/dist/src/shell/galleryPlace.d.ts +20 -0
- package/dist/src/shell/galleryPlace.js +26 -0
- package/dist/src/shell/icons.d.ts +1 -0
- package/dist/src/shell/icons.js +1 -0
- package/dist/src/shell/keyboard.d.ts +14 -12
- package/dist/src/shell/keyboard.js +21 -6
- package/dist/src/shell/laterStrings.d.ts +29 -0
- package/dist/src/shell/laterStrings.js +42 -3
- package/dist/src/shell/mountShell.js +3 -0
- package/dist/src/shell/pageStrings.d.ts +4 -0
- package/dist/src/shell/pageStrings.js +4 -0
- package/dist/src/shell/pageTagView.d.ts +18 -0
- package/dist/src/shell/pageTagView.js +10 -0
- package/dist/src/shell/playMemory.d.ts +14 -0
- package/dist/src/shell/playMemory.js +8 -0
- package/dist/src/shell/propNotes.d.ts +23 -0
- package/dist/src/shell/propNotes.js +37 -0
- package/dist/src/shell/refIndexLoad.d.ts +17 -0
- package/dist/src/shell/refIndexLoad.js +38 -0
- package/dist/src/shell/refParts.d.ts +34 -0
- package/dist/src/shell/refParts.js +9 -0
- package/dist/src/shell/refStory.css +21 -0
- package/dist/src/shell/refStrings.d.ts +13 -0
- package/dist/src/shell/refStrings.js +21 -0
- package/dist/src/shell/search.css +11 -11
- package/dist/src/shell/shell.css +348 -168
- package/dist/src/shell/shellScheme.js +2 -2
- package/dist/src/shell/shortcutHelp.css +64 -0
- package/dist/src/shell/stageArgs.d.ts +53 -0
- package/dist/src/shell/stageArgs.js +64 -0
- package/dist/src/shell/statusBadges.css +51 -0
- package/dist/src/shell/strings.d.ts +7 -16
- package/dist/src/shell/strings.js +12 -17
- package/dist/src/shell/switchGallery.css +104 -0
- package/dist/src/shell/tagFilter.css +46 -0
- package/dist/src/shell/tagView.d.ts +20 -0
- package/dist/src/shell/tagView.js +10 -0
- package/dist/src/shell/useAddressArgs.d.ts +27 -2
- package/dist/src/shell/useAddressArgs.js +71 -14
- package/dist/src/shell/useControlKeys.js +2 -2
- package/dist/src/shell/useEditedProblems.d.ts +6 -0
- package/dist/src/shell/useEditedProblems.js +40 -0
- package/dist/src/shell/useFrameMessages.js +3 -0
- package/dist/src/shell/useListedTree.d.ts +13 -0
- package/dist/src/shell/useListedTree.js +32 -0
- package/dist/src/shell/usePageFrames.js +2 -2
- package/dist/src/shell/usePopover.d.ts +5 -0
- package/dist/src/shell/usePopover.js +7 -1
- package/dist/src/shell/useRefIndex.d.ts +22 -0
- package/dist/src/shell/useRefIndex.js +36 -0
- package/dist/src/shell/useScrollStops.d.ts +20 -0
- package/dist/src/shell/useScrollStops.js +41 -0
- package/dist/src/shell/useShortcutHelp.d.ts +18 -0
- package/dist/src/shell/useShortcutHelp.js +32 -0
- package/dist/src/shell/useSidebarOverlay.d.ts +2 -1
- package/dist/src/shell/useSidebarOverlay.js +12 -4
- package/dist/src/shell/useStoryCode.d.ts +2 -1
- package/dist/src/shell/useStoryCode.js +12 -7
- package/dist/src/shell/useToolbarLayout.d.ts +5 -1
- package/dist/src/shell/useToolbarLayout.js +2 -2
- package/dist/src/shell/useWorstCase.d.ts +8 -2
- package/dist/src/shell/useWorstCase.js +2 -2
- package/dist/src/shell/valueEditors/FileEditor.d.ts +28 -0
- package/dist/src/shell/valueEditors/FileEditor.js +66 -0
- package/dist/src/shell/valueEditors/JsonEditor.d.ts +14 -0
- package/dist/src/shell/valueEditors/JsonEditor.js +53 -0
- package/dist/src/shell/valueEditors/ListEditor.d.ts +24 -0
- package/dist/src/shell/valueEditors/ListEditor.js +82 -0
- package/dist/src/shell/valueEditors/ObjectEditor.d.ts +32 -0
- package/dist/src/shell/valueEditors/ObjectEditor.js +70 -0
- package/dist/src/shell/valueEditors/PropEditor.d.ts +37 -0
- package/dist/src/shell/valueEditors/PropEditor.js +53 -0
- package/dist/src/shell/valueEditors/RecordEditor.d.ts +20 -0
- package/dist/src/shell/valueEditors/RecordEditor.js +84 -0
- package/dist/src/shell/valueEditors/SearchSelect.d.ts +42 -0
- package/dist/src/shell/valueEditors/SearchSelect.js +80 -0
- package/dist/src/shell/valueEditors/TokenEditor.d.ts +49 -0
- package/dist/src/shell/valueEditors/TokenEditor.js +74 -0
- package/dist/src/shell/valueEditors/UnionEditor.d.ts +20 -0
- package/dist/src/shell/valueEditors/UnionEditor.js +64 -0
- package/dist/src/shell/valueEditors/ValueField.d.ts +15 -0
- package/dist/src/shell/valueEditors/ValueField.js +147 -0
- package/dist/src/shell/valueEditors/editorStrings.d.ts +56 -0
- package/dist/src/shell/valueEditors/editorStrings.js +66 -0
- package/dist/src/shell/valueEditors/fieldTypes.d.ts +31 -0
- package/dist/src/shell/valueEditors/fieldTypes.js +0 -0
- package/dist/src/shell/valueEditors/valueEditors.css +153 -0
- package/dist/src/shell/worstArgs.d.ts +8 -5
- package/dist/src/shell/worstArgs.js +14 -7
- package/dist/src/shellTheme.d.ts +1 -1
- package/dist/src/shellTheme.js +15 -1
- package/dist/src/shellTheme.types.d.ts +4 -0
- package/dist/src/shellWord.d.ts +12 -0
- package/dist/src/shellWord.js +15 -0
- package/dist/src/sourceFiles.js +2 -1
- package/dist/src/storyArgTypes.d.ts +57 -0
- package/dist/src/storyArgTypes.js +195 -0
- package/dist/src/storyBackground.js +14 -3
- package/dist/src/storyCheck.d.ts +85 -3
- package/dist/src/storyCheck.js +23 -1
- package/dist/src/storyCodeText.d.ts +46 -0
- package/dist/src/storyCodeText.js +27 -0
- package/dist/src/storyControls.d.ts +100 -0
- package/dist/src/storyControls.js +100 -0
- package/dist/src/storyStatuses.d.ts +55 -0
- package/dist/src/storyStatuses.js +104 -0
- package/dist/src/storyTagNames.d.ts +25 -0
- package/dist/src/storyTagNames.js +25 -0
- package/dist/src/storyTags.d.ts +29 -0
- package/dist/src/storyTags.js +85 -0
- package/dist/src/storyTree.d.ts +13 -0
- package/dist/src/storyTree.js +24 -4
- package/dist/src/storybookArgs.d.ts +24 -0
- package/dist/src/storybookArgs.js +185 -0
- package/dist/src/switches.d.ts +66 -3
- package/dist/src/switches.js +42 -1
- package/dist/src/valueSchema.d.ts +28 -0
- package/dist/src/valueSchema.js +196 -0
- package/dist/src/valueShape.d.ts +94 -0
- package/dist/src/valueShape.js +25 -0
- package/dist/src/viewState.d.ts +8 -3
- package/dist/src/viewState.js +27 -10
- package/dist/src/vite/adapter.d.ts +40 -2
- package/dist/src/vite/adapter.js +1 -0
- package/dist/src/vite/agent.types.d.ts +347 -7
- package/dist/src/vite/agentApi.d.ts +23 -0
- package/dist/src/vite/agentApi.js +90 -17
- package/dist/src/vite/agentIndex.d.ts +0 -2
- package/dist/src/vite/agentIndex.js +5 -6
- package/dist/src/vite/agentRoutes.d.ts +20 -1
- package/dist/src/vite/agentRoutes.js +106 -65
- package/dist/src/vite/agentStatuses.d.ts +42 -0
- package/dist/src/vite/agentStatuses.js +67 -0
- package/dist/src/vite/agentStoryCode.d.ts +48 -0
- package/dist/src/vite/agentStoryCode.js +38 -0
- package/dist/src/vite/agentVisibility.d.ts +47 -0
- package/dist/src/vite/agentVisibility.js +174 -0
- package/dist/src/vite/appAudit.d.ts +134 -0
- package/dist/src/vite/appAudit.js +333 -0
- package/dist/src/vite/appBuildOption.d.ts +53 -0
- package/dist/src/vite/appBuildOption.js +55 -0
- package/dist/src/vite/appTokenFiles.d.ts +92 -0
- package/dist/src/vite/appTokenFiles.js +433 -0
- package/dist/src/vite/appTokens.d.ts +23 -0
- package/dist/src/vite/appTokens.js +275 -0
- package/dist/src/vite/builtTexts.d.ts +10 -0
- package/dist/src/vite/builtTexts.js +59 -22
- package/dist/src/vite/catalogueConfig.d.ts +3 -1
- package/dist/src/vite/catalogueConfig.js +15 -3
- package/dist/src/vite/changeCoverage.d.ts +74 -0
- package/dist/src/vite/changeCoverage.js +120 -0
- package/dist/src/vite/changedRoute.d.ts +30 -0
- package/dist/src/vite/changedRoute.js +197 -0
- package/dist/src/vite/checkCode.d.ts +105 -3
- package/dist/src/vite/checkCode.js +323 -36
- package/dist/src/vite/checkoutWatch.d.ts +97 -0
- package/dist/src/vite/checkoutWatch.js +287 -0
- package/dist/src/vite/closeWatch.d.ts +8 -0
- package/dist/src/vite/closeWatch.js +1 -1
- package/dist/src/vite/componentCache.d.ts +8 -0
- package/dist/src/vite/componentCache.js +66 -0
- package/dist/src/vite/componentGraph.d.ts +71 -0
- package/dist/src/vite/componentGraph.js +133 -0
- package/dist/src/vite/componentRules.d.ts +33 -0
- package/dist/src/vite/componentRules.js +105 -0
- package/dist/src/vite/componentScan.d.ts +69 -0
- package/dist/src/vite/componentScan.js +418 -0
- package/dist/src/vite/compositionCheck.d.ts +15 -0
- package/dist/src/vite/compositionCheck.js +243 -0
- package/dist/src/vite/config.d.ts +32 -1
- package/dist/src/vite/configFile.d.ts +14 -2
- package/dist/src/vite/configFile.js +124 -40
- package/dist/src/vite/configKeys.d.ts +15 -0
- package/dist/src/vite/configKeys.js +171 -0
- package/dist/src/vite/configLoadError.d.ts +23 -0
- package/dist/src/vite/configLoadError.js +71 -0
- package/dist/src/vite/cssDeclarations.d.ts +71 -0
- package/dist/src/vite/cssDeclarations.js +290 -0
- package/dist/src/vite/cssImports.d.ts +14 -0
- package/dist/src/vite/cssImports.js +48 -0
- package/dist/src/vite/cssReferences.d.ts +54 -0
- package/dist/src/vite/cssReferences.js +157 -0
- package/dist/src/vite/cssTokenValues.d.ts +50 -0
- package/dist/src/vite/cssTokenValues.js +233 -0
- package/dist/src/vite/designTokens.d.ts +24 -0
- package/dist/src/vite/designTokens.js +5 -1
- package/dist/src/vite/docsExtractor.d.ts +9 -3
- package/dist/src/vite/docsExtractor.js +11 -3
- package/dist/src/vite/docsProgram.d.ts +17 -4
- package/dist/src/vite/docsProgram.js +52 -11
- package/dist/src/vite/docsWorker.js +7 -2
- package/dist/src/vite/emptyStoryGlobs.d.ts +41 -0
- package/dist/src/vite/emptyStoryGlobs.js +52 -0
- package/dist/src/vite/fileImports.d.ts +17 -0
- package/dist/src/vite/fileImports.js +57 -0
- package/dist/src/vite/findComponent.d.ts +10 -2
- package/dist/src/vite/findComponent.js +27 -22
- package/dist/src/vite/findRoute.js +3 -2
- package/dist/src/vite/forcedStates.d.ts +9 -2
- package/dist/src/vite/forcedStates.js +12 -3
- package/dist/src/vite/forgebookPlugin.d.ts +2 -0
- package/dist/src/vite/forgebookPlugin.js +52 -72
- package/dist/src/vite/frameErrors.d.ts +19 -0
- package/dist/src/vite/frameErrors.js +70 -0
- package/dist/src/vite/framesServer.d.ts +12 -0
- package/dist/src/vite/framesServer.js +21 -4
- package/dist/src/vite/functionComponentDocs.js +81 -12
- package/dist/src/vite/generatedModules.js +4 -2
- package/dist/src/vite/gitChange.d.ts +38 -5
- package/dist/src/vite/gitChange.js +71 -11
- package/dist/src/vite/hiddenStoryFiles.d.ts +23 -0
- package/dist/src/vite/hiddenStoryFiles.js +107 -0
- package/dist/src/vite/hotSnippets.d.ts +38 -14
- package/dist/src/vite/hotSnippets.js +137 -39
- package/dist/src/vite/importedNames.d.ts +37 -0
- package/dist/src/vite/importedNames.js +138 -0
- package/dist/src/vite/indexFields.d.ts +6 -0
- package/dist/src/vite/indexFields.js +21 -0
- package/dist/src/vite/indexStoryFile.d.ts +8 -1
- package/dist/src/vite/indexStoryFile.js +22 -157
- package/dist/src/vite/jsxCallSites.d.ts +33 -0
- package/dist/src/vite/jsxCallSites.js +200 -0
- package/dist/src/vite/jsxInScripts.d.ts +67 -0
- package/dist/src/vite/jsxInScripts.js +138 -0
- package/dist/src/vite/jsxTree.d.ts +20 -0
- package/dist/src/vite/jsxTree.js +163 -0
- package/dist/src/vite/lineAndColumn.d.ts +13 -0
- package/dist/src/vite/lineAndColumn.js +18 -0
- package/dist/src/vite/lookAlikes.d.ts +65 -0
- package/dist/src/vite/lookAlikes.js +228 -0
- package/dist/src/vite/nearestToken.d.ts +17 -0
- package/dist/src/vite/nearestToken.js +9 -2
- package/dist/src/vite/packageEntries.d.ts +42 -0
- package/dist/src/vite/packageEntries.js +127 -0
- package/dist/src/vite/pages.d.ts +9 -1
- package/dist/src/vite/pages.js +108 -4
- package/dist/src/vite/pluginConfig.js +4 -0
- package/dist/src/vite/pluginIds.d.ts +2 -0
- package/dist/src/vite/pluginIds.js +5 -1
- package/dist/src/vite/pluginModules.d.ts +81 -11
- package/dist/src/vite/pluginModules.js +109 -21
- package/dist/src/vite/prebundled.d.ts +57 -15
- package/dist/src/vite/prebundled.js +80 -0
- package/dist/src/vite/previewTags.d.ts +25 -0
- package/dist/src/vite/previewTags.js +56 -0
- package/dist/src/vite/projectFiles.d.ts +54 -0
- package/dist/src/vite/projectFiles.js +116 -0
- package/dist/src/vite/projectPostcss.d.ts +70 -0
- package/dist/src/vite/projectPostcss.js +250 -0
- package/dist/src/vite/propDocs.d.ts +12 -0
- package/dist/src/vite/propDocs.js +130 -58
- package/dist/src/vite/propKind.d.ts +40 -0
- package/dist/src/vite/propKind.js +71 -0
- package/dist/src/vite/propTypeNotes.d.ts +22 -0
- package/dist/src/vite/propTypeNotes.js +51 -0
- package/dist/src/vite/publishedExports.d.ts +27 -0
- package/dist/src/vite/publishedExports.js +298 -0
- package/dist/src/vite/realFolder.d.ts +6 -0
- package/dist/src/vite/realFolder.js +25 -0
- package/dist/src/vite/refsConfig.d.ts +39 -0
- package/dist/src/vite/refsConfig.js +54 -0
- package/dist/src/vite/reuseCheck.d.ts +45 -0
- package/dist/src/vite/reuseCheck.js +260 -0
- package/dist/src/vite/runningVersions.d.ts +33 -0
- package/dist/src/vite/runningVersions.js +25 -0
- package/dist/src/vite/scriptProblems.d.ts +60 -0
- package/dist/src/vite/scriptProblems.js +155 -0
- package/dist/src/vite/searchModule.js +3 -2
- package/dist/src/vite/searchRoute.js +4 -2
- package/dist/src/vite/shapeDocs.d.ts +36 -0
- package/dist/src/vite/shapeDocs.js +177 -0
- package/dist/src/vite/singleFileComponent.d.ts +17 -0
- package/dist/src/vite/singleFileComponent.js +83 -0
- package/dist/src/vite/sourceText.d.ts +34 -0
- package/dist/src/vite/sourceText.js +72 -0
- package/dist/src/vite/sourceTokens.d.ts +7 -0
- package/dist/src/vite/sourceTokens.js +7 -2
- package/dist/src/vite/stackLines.d.ts +49 -0
- package/dist/src/vite/stackLines.js +96 -0
- package/dist/src/vite/storyCode.d.ts +22 -7
- package/dist/src/vite/storyCode.js +263 -37
- package/dist/src/vite/storyCoverage.d.ts +48 -0
- package/dist/src/vite/storyCoverage.js +179 -0
- package/dist/src/vite/storyFilesModule.d.ts +48 -3
- package/dist/src/vite/storyFilesModule.js +105 -15
- package/dist/src/vite/storyPatterns.d.ts +31 -0
- package/dist/src/vite/storyPatterns.js +140 -5
- package/dist/src/vite/storyReach.d.ts +10 -2
- package/dist/src/vite/storyReach.js +15 -11
- package/dist/src/vite/storyTopLevel.d.ts +51 -0
- package/dist/src/vite/storyTopLevel.js +146 -0
- package/dist/src/vite/storylessRoutes.d.ts +4 -3
- package/dist/src/vite/storylessRoutes.js +210 -19
- package/dist/src/vite/switchValuesCheck.d.ts +19 -0
- package/dist/src/vite/switchValuesCheck.js +164 -0
- package/dist/src/vite/switchesDocument.d.ts +15 -0
- package/dist/src/vite/switchesDocument.js +47 -0
- package/dist/src/vite/tailwindConfigTokens.d.ts +41 -0
- package/dist/src/vite/tailwindConfigTokens.js +306 -0
- package/dist/src/vite/textImports.d.ts +15 -0
- package/dist/src/vite/textImports.js +58 -0
- package/dist/src/vite/textModules.d.ts +6 -5
- package/dist/src/vite/textModules.js +13 -8
- package/dist/src/vite/tokenAnswers.d.ts +6 -2
- package/dist/src/vite/tokenAnswers.js +11 -7
- package/dist/src/vite/typeParameters.d.ts +83 -0
- package/dist/src/vite/typeParameters.js +270 -0
- package/dist/src/vite/unionProps.d.ts +37 -0
- package/dist/src/vite/unionProps.js +121 -0
- package/dist/src/vite/unresolvedTokens.d.ts +20 -0
- package/dist/src/vite/unresolvedTokens.js +193 -0
- package/dist/src/vite/usageGraph.js +10 -52
- package/dist/src/vite/usageGuidance.d.ts +9 -2
- package/dist/src/vite/usageGuidance.js +36 -8
- package/dist/src/vite/usageImports.d.ts +35 -0
- package/dist/src/vite/usageImports.js +280 -0
- package/dist/src/vite/visibilityRoute.d.ts +16 -0
- package/dist/src/vite/visibilityRoute.js +320 -0
- package/dist/src/worstCase.d.ts +2 -0
- package/dist/src/worstCase.js +2 -0
- package/package.json +28 -8
package/README.md
CHANGED
|
@@ -9,7 +9,12 @@ per story, switches for theme, contrast, motion and language, and a props table
|
|
|
9
9
|
> and every change is in the
|
|
10
10
|
> [changelog](https://github.com/ensiria/forgebook/blob/develop/packages/forgebook/CHANGELOG.md).
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
New to it? The [documentation](https://github.com/ensiria/forgebook/blob/develop/docs/README.md) starts with
|
|
13
|
+
[getting started](https://github.com/ensiria/forgebook/blob/develop/docs/getting-started.md) and has the how-to guides and
|
|
14
|
+
[troubleshooting](https://github.com/ensiria/forgebook/blob/develop/docs/troubleshooting.md); this README is the full
|
|
15
|
+
reference.
|
|
16
|
+
|
|
17
|
+
**Requirements:** Node 22.18 or later (the catalogue is built on it; other bundlers are not supported) · React 19 through
|
|
13
18
|
[`@forgebook/react`](https://www.npmjs.com/package/@forgebook/react), Vue 3.5 through
|
|
14
19
|
[`@forgebook/vue`](https://www.npmjs.com/package/@forgebook/vue) (a Nuxt project's through
|
|
15
20
|
[`@forgebook/nuxt`](https://www.npmjs.com/package/@forgebook/nuxt)), Svelte 5 through
|
|
@@ -47,9 +52,108 @@ sidebar's index, kept with the files they were read from, so a later start answe
|
|
|
47
52
|
TypeScript until one of those files, a configuration or the lock file changes. Removing `node_modules/.vite` starts from
|
|
48
53
|
nothing.
|
|
49
54
|
|
|
55
|
+
## Start with one command
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
npx forgebook init
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
It asks nothing, so an agent can run it. In the folder of the package that holds the components, it reads the framework
|
|
62
|
+
(React, Vue, Svelte, Solid, Preact, Lit, Angular, Next.js, Nuxt, SvelteKit, React Native Web) and the package manager
|
|
63
|
+
from the project's files, and writes:
|
|
64
|
+
|
|
65
|
+
- `forgebook.config.ts`: the adapter, the title, and story globs over the folders that exist;
|
|
66
|
+
- `forgebook.preview.ts` (`.tsx` where stories are), when the app loads global style sheets: the imports of its entry
|
|
67
|
+
(Vite's `index.css`, a Next.js root layout's `globals.css`, a SvelteKit layout's, `angular.json`'s `styles`), since
|
|
68
|
+
stories look wrong without them. Nuxt's adapter reads `css` from `nuxt.config` itself, so it needs none.
|
|
69
|
+
|
|
70
|
+
Then it prints what is left, each thing named, and the next commands. The packages to add are in the exact command of
|
|
71
|
+
the project's package manager; `--install` runs it. It lists React even on Vue, say, because Forgebook's shell needs it and not every package manager installs a peer by
|
|
72
|
+
itself. Vite is Forgebook's own dependency: a project on an older one installs it beside its own, which stays as it is.
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
npx forgebook new src/Badge.tsx # a component's first stories
|
|
76
|
+
npx forgebook dev # the catalogue, live
|
|
77
|
+
npx forgebook stories --json # what it lists, and what did not read
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
It writes nothing and exits with an error when:
|
|
81
|
+
|
|
82
|
+
- a file is already there (a configuration or a preview, under any name it is read by): `--force` replaces it;
|
|
83
|
+
- `package.json` names no framework it supports, or two that stand on their own: `--framework <name>` chooses (`html`
|
|
84
|
+
for no framework). An adapter already in `package.json` settles it;
|
|
85
|
+
- a framework, React or Vite in `package.json` cannot reach the version Forgebook takes: the message names each.
|
|
86
|
+
|
|
87
|
+
- the project has a `.storybook` (or `.rnstorybook`) folder: `--from storybook` takes [that route](#from-storybook),
|
|
88
|
+
which loads its `main` and so runs its code, or `--framework <name>` starts from the dependencies;
|
|
89
|
+
- a file it would write is a link: it never writes through one, even with `--force`.
|
|
90
|
+
|
|
91
|
+
`init` runs no code of the project unless `--from storybook` or `--install` asks it to; `--install` is an install like
|
|
92
|
+
any other, with the project's package manager settings and the packages' scripts. `--json` prints the result as a document, a refusal
|
|
93
|
+
too (`refused`, `message`, `hint`, `supported`, exit code 1; a wrong command line is exit code 2, with no document), with `nextSteps` as `{ kind, file, message }`: `kind` is `style-sheet`,
|
|
94
|
+
`provider`, `portal`, `head`, `font`, `postcss`, `setup`, `version` or `storybook`.
|
|
95
|
+
|
|
96
|
+
### What init cannot work out
|
|
97
|
+
|
|
98
|
+
A catalogue runs no entry of the app, so what one does for the app does not happen for a story. Each of these is
|
|
99
|
+
reported with the file it was read from:
|
|
100
|
+
|
|
101
|
+
- **Providers**, the components that wrap the app, and what it installs (`app.use(router)`, `provideRouter()`): a decorator
|
|
102
|
+
in the preview gives them to the stories that need them.
|
|
103
|
+
- **Portals**: an element beside the app's root in its page (`#modal-root`). A decorator adds it.
|
|
104
|
+
- **The page's head**: a font's or a CDN's `<link>` goes to `head` in the configuration.
|
|
105
|
+
|
|
106
|
+
### The app's build
|
|
107
|
+
|
|
108
|
+
A catalogue draws your components with your app's build, with nothing to write:
|
|
109
|
+
|
|
110
|
+
| What | Where it is read from | Runs your code? |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| Aliases | `tsconfig.json`'s `paths` and `baseUrl` (through `extends` and `references`) | No: read as text, Vite resolves them |
|
|
113
|
+
| Aliases, `define`, Sass options, `.env` settings, your Vite plugins (Tailwind's, SVGR…) | `vite.config.*` beside the catalogue's configuration | **Yes**, as `vite dev` does |
|
|
114
|
+
| Tailwind before 4, Autoprefixer, any PostCSS plugin | `postcss.config.*`, `.postcssrc*`, `package.json`'s `postcss`, up to the repository's root | **Yes**: a script, and the plugins any of them names |
|
|
115
|
+
|
|
116
|
+
So every command that starts the catalogue runs those files, on purpose: `forgebook stories` and `check` as well as `dev`.
|
|
117
|
+
The catalogue's own configuration and your components already run there, and this is the build you run every day. A first
|
|
118
|
+
`dev` start loads a `vite.config` twice, as its pre-bundling runs in a process of its own. `forgebook init` never runs them:
|
|
119
|
+
it reads their names and reports them (`appBuild` in `--json`, with `runsCode`). `forgebook dev` prints what it read; what
|
|
120
|
+
could not be read is said on stderr by every command.
|
|
121
|
+
|
|
122
|
+
From a `vite.config`, a catalogue takes how modules and style sheets resolve and compile, and never the app's page,
|
|
123
|
+
server or output (`root`, `base`, `build`, `server`), from the file and from what its plugins' `config` hooks return; a
|
|
124
|
+
plugin that changes the configuration in place is not stopped. A plugin's `configureServer`, where one starts a backend or a
|
|
125
|
+
watcher, runs on `forgebook dev` and not on a command's own server (`stories`, `check`, `verify`, `mcp`). Its plugins go in, apart from
|
|
126
|
+
two kinds: the framework's own, which the adapter brings, and those known to build an app rather than draw a component
|
|
127
|
+
(routers, service workers, source-map uploads, type checkers). That second list is names, so an upload plugin it does not
|
|
128
|
+
know runs on `forgebook build`: name it in `skipPlugins`. A plugin's `transformIndexHtml` reaches the frames, not the
|
|
129
|
+
catalogue's page. A `define` that writes out a whole `process.env` is left out, since a catalogue bundles code the app does
|
|
130
|
+
not. A file that fails to load is said with what to do; stories that do not need it still draw.
|
|
131
|
+
|
|
132
|
+
Not read: the options of the framework's own plugin (`react({ jsxImportSource })` goes to the adapter), and `css.modules`
|
|
133
|
+
(Lightning CSS has `css.lightningcss.cssModules`, and no `localsConvention`); both are said. A workspace's other packages'
|
|
134
|
+
`tsconfig` paths are not looked for, only the catalogue's own folder's, as `extends` and `references` lead on from it.
|
|
135
|
+
|
|
136
|
+
A `vite.config` holding only Vite's import and the framework's plugin is not loaded at all, so a new project pays for
|
|
137
|
+
none of this. Next.js, Nuxt, SvelteKit, Angular and React Native read what is theirs themselves, as their own sections say.
|
|
138
|
+
|
|
139
|
+
To turn it off:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
export default defineConfig({
|
|
143
|
+
// …
|
|
144
|
+
appBuild: false // or each part:
|
|
145
|
+
// appBuild: { viteConfig: false, postcss: false, tsconfigPaths: false, skipPlugins: ["some-plugin"], keepPlugins: ["react-router"] }
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
A plugin's name is the one `forgebook dev` prints, not its package's. A PostCSS configuration in YAML is not read (`init`
|
|
150
|
+
says so). PostCSS runs on plain CSS and on a component's `<style>` block, with the sheets they import by relative path; a
|
|
151
|
+
Sass file goes through its own compiler alone. When a framework plugin or the app's `vite.config` sets
|
|
152
|
+
`css.transformer: "postcss"`, Vite runs the project's PostCSS itself and the catalogue does not run it a second time.
|
|
153
|
+
|
|
50
154
|
## A first catalogue
|
|
51
155
|
|
|
52
|
-
A component, with its props typed and documented:
|
|
156
|
+
The same by hand, which is also what the command writes. A component, with its props typed and documented:
|
|
53
157
|
|
|
54
158
|
```tsx
|
|
55
159
|
// src/Badge.tsx
|
|
@@ -137,16 +241,20 @@ too, with a warning.
|
|
|
137
241
|
edit that adds, renames or retitles a story reloads the pages, as an edit of an Angular story file does. On macOS
|
|
138
242
|
the files you work on (the story files the open frames show, and each file they load once you have edited it) are
|
|
139
243
|
watched one by one, so an edit reaches the page without the system's change events' wait of a tenth of a second or
|
|
140
|
-
more.
|
|
244
|
+
more. A checkout that moves the repository to another commit (`git switch`, `git reset --hard`, a pull) is heard
|
|
245
|
+
through git's own files, and each file it wrote reaches the dev server at once: the branch's stories, a removed one
|
|
246
|
+
gone, a new folder's shown, with no restart. Where the system's change events come late or never (a container's mounted volume, a Windows drive under WSL,
|
|
141
247
|
a network drive), `forgebook dev --poll` checks the files itself.
|
|
142
248
|
- **Switches** for theme, contrast and motion, and the languages you declare; a library adds its own (density, brand)
|
|
143
249
|
through a preset. A direction switch draws every story right to left, and a language declared `dir: "rtl"` does
|
|
144
250
|
it by itself.
|
|
145
251
|
- **A toolbar each reader arranges**: whoever uses the catalogue pins the controls they use to the toolbar, orders
|
|
146
252
|
them and hides the rest in the View menu; their browser keeps it ([below](#the-toolbar)).
|
|
147
|
-
- **A light and a dark catalogue**: the reader picks Light, Dark or System at the sidebar's foot
|
|
148
|
-
it); `appearance` in the configuration sets the default. The stories keep their own theme, from the view's switches.
|
|
149
|
-
- **Keys and named views**:
|
|
253
|
+
- **A light and a dark catalogue**: the reader picks Light, Dark or System in the Interface menu at the sidebar's foot
|
|
254
|
+
(their browser keeps it); `appearance` in the configuration sets the default. The stories keep their own theme, from the view's switches.
|
|
255
|
+
- **Keys and named views**: `?` lists the keys, as does the menu at the sidebar's foot, where a reader can turn off every
|
|
256
|
+
key without a modifier for this browser; a key pressed inside a story, in a text field or in a custom element stays
|
|
257
|
+
theirs. A key cycles a control's values (`t` the theme, Shift+`t` back); the catalogue sets the
|
|
150
258
|
key of any control (`keys: { width: "w" }`), a reader changes theirs in the View menu. The View menu's values are
|
|
151
259
|
saved under a name and picked again; `views` in the configuration offers some to every reader. A dot marks each
|
|
152
260
|
control whose value is not its default.
|
|
@@ -157,7 +265,8 @@ too, with a warning.
|
|
|
157
265
|
- **Widths** to draw a story at, and a **compare** view that draws the same story once per value of one or two
|
|
158
266
|
switches (every theme at both contrasts), per text size or per style sheet, side by side.
|
|
159
267
|
- **Props from the types**: the API table and the panel's controls come from the component's TypeScript props and
|
|
160
|
-
their TSDoc, nothing written twice
|
|
268
|
+
their TSDoc, nothing written twice: objects and lists as nested fields, mixed unions, tokens and named values too
|
|
269
|
+
([below](#the-panels-controls)).
|
|
161
270
|
- **Props in groups**: a component with many props lists them under the groups their TSDoc names (`@group Paging`),
|
|
162
271
|
and a prop typed with a documented object (`texts?: FinderTexts`) unfolds to list its members
|
|
163
272
|
([below](#props-in-groups)).
|
|
@@ -165,11 +274,20 @@ too, with a warning.
|
|
|
165
274
|
reader turns the ordering off with a switch ([below](#required-props-first)).
|
|
166
275
|
- **Shared values, checked**: the values set in the Props tab stay in the address (`args=`), so a link shares them.
|
|
167
276
|
Since whoever writes a link chooses them, a story receives only the props it declares (its component's props, its
|
|
168
|
-
`argTypes`, its own args) that the panel lets a person set, each with a value of that prop's type
|
|
169
|
-
(`onclick`, `innerHTML`, a text for a number) is left out and listed above
|
|
170
|
-
from the address. A value that is code
|
|
277
|
+
`argTypes`, its own args) that the panel lets a person set, each with a value of that prop's type, an object with only
|
|
278
|
+
the fields its type declares; anything else (`onclick`, `innerHTML`, a text for a number) is left out and listed above
|
|
279
|
+
the story, with a button that removes it from the address, as is one that cannot be read at all. A value that is code
|
|
280
|
+
never comes from a link.
|
|
281
|
+
- **Code as you wrote it**: the Code tab and Show code give a story's `render` as written in its file (its own, else
|
|
282
|
+
the meta's; one kept in a constant or a function of the file shows as that code), with its hooks and compound
|
|
283
|
+
components as they are. A story drawn from its args shows its component with each arg the file writes as code
|
|
284
|
+
(`onClick: fn()`, `icon: <IconStar />`, a constant of the file) as written, spreads of the file's objects and of
|
|
285
|
+
another story's args followed, and the plain values as they are now. A value set in the Props tab is written from
|
|
286
|
+
that value. `forgebook story --json` gives the same text as `code`. Vue and Svelte bind such an arg as written
|
|
287
|
+
(`:on-close="fn()"`); Angular, HTML and Web Components still write the plain values only. A story made by a
|
|
288
|
+
helper, imported from another file or written in CSF 4 (`meta.story(…)`) is written from its values.
|
|
171
289
|
- **The worst case in one press**: the Props tab's Worst case button gives every text prop a long text with a word too
|
|
172
|
-
long to break
|
|
290
|
+
long to break, every number a huge one and every list many items, nested ones too, kept in the address; the elements that then spill out of their parents
|
|
173
291
|
are outlined and counted, as the browser tests find them. Reset undoes it.
|
|
174
292
|
- **Examples that fill the page**: on a component's page, any story's box opens over the page, at a width of its own,
|
|
175
293
|
without loading its story again.
|
|
@@ -228,6 +346,64 @@ shows nowhere, not even on the toolbar, and keeps its default (the width can sti
|
|
|
228
346
|
may set both; the catalogue's `toolbar` wins, and a control is off when any of them turns it off. A name in `toolbar`
|
|
229
347
|
that is no control at all stops the catalogue with an error saying which.
|
|
230
348
|
|
|
349
|
+
### Several visual themes
|
|
350
|
+
|
|
351
|
+
A library with several looks offers them as a switch whose `control` is a gallery: a menu whose items show each look, so a
|
|
352
|
+
reader sees it before applying it.
|
|
353
|
+
|
|
354
|
+
```ts
|
|
355
|
+
switches: [
|
|
356
|
+
{ ...themeSwitch(), label: "Appearance" },
|
|
357
|
+
{
|
|
358
|
+
id: "look",
|
|
359
|
+
label: "Theme",
|
|
360
|
+
control: "gallery",
|
|
361
|
+
apply: { kind: "attribute", name: "data-look" },
|
|
362
|
+
default: "plain",
|
|
363
|
+
values: [
|
|
364
|
+
{ value: "plain", label: "Plain", isUnset: true },
|
|
365
|
+
{ value: "paper", label: "Paper", preview: { image: "previews/paper.png" }, sheet: "looks/paper.css" },
|
|
366
|
+
{
|
|
367
|
+
value: "night",
|
|
368
|
+
label: "Night",
|
|
369
|
+
preview: { swatches: ["#0b1020", "#7c8cff"], font: "Harbor Sans" },
|
|
370
|
+
implies: { theme: "dark" },
|
|
371
|
+
sheet: "looks/night.css"
|
|
372
|
+
}
|
|
373
|
+
]
|
|
374
|
+
}
|
|
375
|
+
]
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
- **`control: "gallery"`** beside `segments` and `list`. The first load carries only its button: the menu's code, the
|
|
379
|
+
previews and their pictures load when it first opens. Radio buttons, as the shell's other menus: one tab stop, the arrow
|
|
380
|
+
keys move and choose, Escape closes. Each item is named by the value's `label`; a value with no preview shows its first
|
|
381
|
+
letter.
|
|
382
|
+
- **`preview`** is `{ image }`, a file by its path from the configuration's folder (absolute in a preset), or
|
|
383
|
+
`{ swatches, font }`, 1 to 8 plain colours and a font's name. A font is only a name: drawn in it where the reader has it
|
|
384
|
+
installed, never fetched. A preview loads nothing from an address: the configuration's check refuses one, and a `url()`.
|
|
385
|
+
- **`implies`** sets other switches while the value is drawn: a look drawn only for dark selects dark by itself. The
|
|
386
|
+
implied control is off and says what sets it, and its key does nothing. The address keeps the reader's own value, which
|
|
387
|
+
comes back when the value is left: `?look=night&theme=light` draws dark, and the link stays as written. An implied
|
|
388
|
+
value wins over the address and over a story's `globals`; between two values that imply different values of one switch,
|
|
389
|
+
the switch declared first wins; an implied value implies nothing further, which the check enforces. Compare leaves
|
|
390
|
+
out a cell it contradicts (with `look,theme`, Night only at Dark) and does not offer a switch implied now.
|
|
391
|
+
- **`sheet`** is a style sheet of that value alone, processed by Vite in dev and in a build, and fetched only while
|
|
392
|
+
the value is drawn. It comes after the source's sheets, the switches' in the order declared, and only with Styles › Full,
|
|
393
|
+
as the source's theme. Its path may not be an address; its contents are your own CSS, so what it imports is yours to
|
|
394
|
+
decide, as in a source's sheets. On a change the old sheet stays until the new one has loaded, and the frame's root changes with it,
|
|
395
|
+
so a story is never drawn with neither.
|
|
396
|
+
|
|
397
|
+
A source's page colour can follow such a switch, so a story's box waits on the colour it will have:
|
|
398
|
+
`background: { light, dark, bySwitch: { look: { night: { dark: "#0b1020" } } } }`. The first declared switch whose value
|
|
399
|
+
gives the scheme's colour wins; otherwise `light` and `dark`.
|
|
400
|
+
|
|
401
|
+
The catalogue's own menu, at the sidebar's foot, is named Interface, so it reads apart from a switch named Appearance: it
|
|
402
|
+
colours the catalogue around the stories, never the stories. `strings: { appearance: "…" }` renames it; the
|
|
403
|
+
configuration's `appearance` key and `data-forgebook-appearance` keep their names.
|
|
404
|
+
|
|
405
|
+
### Backgrounds
|
|
406
|
+
|
|
231
407
|
The Background control offers Forgebook's five pictures and the catalogue's own:
|
|
232
408
|
`backgrounds: { builtIn: ["checkerboard"], custom: [{ id: "paper", label: "Paper", light: { color: "#fffdf5" } }] }`
|
|
233
409
|
keeps one picture and adds a plain paper colour (`layers` puts CSS backgrounds over it, `dark` sets another look for the
|
|
@@ -434,8 +610,8 @@ export type FinderProps = {
|
|
|
434
610
|
|
|
435
611
|
- The API reference lists the props that name no group first, then each group under its name, in the order the
|
|
436
612
|
groups first appear among the props; the events keep their own heading after them. The Props tab does the same: the
|
|
437
|
-
props that name no group
|
|
438
|
-
cannot set.
|
|
613
|
+
props that name no group in the order the source declares them, whatever their control, then each group with its
|
|
614
|
+
controls in that order and the props it cannot set.
|
|
439
615
|
- Every group starts open; a reader folds one away from its name.
|
|
440
616
|
- To show some groups first, name them in order: `propGroups: ["Selection", "Paging"]` in the configuration for every
|
|
441
617
|
component, or `parameters: { propGroups: ["Texts"] }` in a component's meta, written as a list of plain strings.
|
|
@@ -446,6 +622,33 @@ export type FinderProps = {
|
|
|
446
622
|
- With `eslint-plugin-tsdoc`, declare the tag in your `tsdoc.json`:
|
|
447
623
|
`{ "tagName": "@group", "syntaxKind": "block", "allowMultiple": false }`.
|
|
448
624
|
|
|
625
|
+
## Props a type cannot list plainly
|
|
626
|
+
|
|
627
|
+
Some props types say more than a list of props. The API reference reads them as TypeScript does, and says what it
|
|
628
|
+
could not read:
|
|
629
|
+
|
|
630
|
+
```tsx
|
|
631
|
+
export type ActionProps<TItem> = (
|
|
632
|
+
| { readonly kind: "link"; readonly href: string }
|
|
633
|
+
| { readonly kind: "button"; readonly onClick: () => void }
|
|
634
|
+
) & { readonly label: string; readonly items?: readonly TItem[] };
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
- **A union of variants** lists every variant's props, not only the ones they share. A prop only some variants declare
|
|
638
|
+
is never marked required, and says when it applies: `href` reads "Only when `kind` is `"link"`". The discriminant is
|
|
639
|
+
the shared prop each variant gives one literal value (or leaves out), the one with the most values when several do.
|
|
640
|
+
A prop of a variant that takes the discriminant left out (`mode?: "single"`) says "or left out".
|
|
641
|
+
A prop the variants type differently shows all their types (`Date | Date[] | DateRange`).
|
|
642
|
+
- **A generic component** writes each type parameter it leaves open as the component's constraint, else its default,
|
|
643
|
+
else `unknown`, and says which under the prop: `items` is `readonly unknown[]`, "`TItem` is generic, with no
|
|
644
|
+
constraint". A props type the component fills in (`BaseProps<string>`) is read with what it was given.
|
|
645
|
+
- **A type it cannot read** says so instead of showing nothing: a name the checker cannot find (a missing import), a
|
|
646
|
+
prop typed `any`, props typed `any`, or a component whose props all come from outside its package (an element's
|
|
647
|
+
attributes, which the reference leaves out).
|
|
648
|
+
- `forgebook story --json` and `get_story` carry the same as `onlyWhen`, `notes` on a prop and `propsNote` on the
|
|
649
|
+
component; `forgebook story` writes `href (when kind is link)`, and `CATALOG.md` `href:string[kind=link]` (`[mode?=single]`
|
|
650
|
+
when it also applies with `mode` left out).
|
|
651
|
+
|
|
449
652
|
## Required props first
|
|
450
653
|
|
|
451
654
|
The API reference and the Props tab list a component's required props before the others, so a reader sees at once
|
|
@@ -474,6 +677,104 @@ export default { component: Finder, parameters: { requiredFirst: false } } satis
|
|
|
474
677
|
- Agents get the props in the order of the source and the names of the required ones at the top of
|
|
475
678
|
`forgebook story --json` and `get_story` (`requiredProps`): the setting shapes the pages only.
|
|
476
679
|
|
|
680
|
+
## The panel's controls
|
|
681
|
+
|
|
682
|
+
The Props tab draws a control for each prop from its type, with nothing to write:
|
|
683
|
+
|
|
684
|
+
| The prop's type | Its control |
|
|
685
|
+
|---|---|
|
|
686
|
+
| `string`, `number`, `boolean` | a field, a number, a toggle; a text with a line break becomes a text area |
|
|
687
|
+
| `"sm" \| "md"` | buttons or a list; a search past 12 values |
|
|
688
|
+
| `readonly string[]`, `{ a: string; b?: number }` | nested fields, with add, remove and reorder by buttons, so a keyboard does it too |
|
|
689
|
+
| `[number, number]`, `Record<string, number>` | a field per item; names and values |
|
|
690
|
+
| `"sm" \| number`, `string \| string[]` | choose the type, then the value |
|
|
691
|
+
| `unknown`, `object`, a type too deep or wider than 40 fields | JSON, checked as it is typed |
|
|
692
|
+
| a component, a function, a node | what the source's `namedValues` offer for its type, searched by name; a node takes a text too |
|
|
693
|
+
|
|
694
|
+
A story names another control the way Storybook's do, and a few of Forgebook's own:
|
|
695
|
+
|
|
696
|
+
```ts
|
|
697
|
+
argTypes: {
|
|
698
|
+
note: { control: "textarea" },
|
|
699
|
+
options: { control: "object" }, // fields from the type, else from the story's own arg, else JSON
|
|
700
|
+
accent: { control: { type: "token", of: "color" } }, // from the catalogue's `tokens`
|
|
701
|
+
gap: { control: { type: "token", of: "space", format: "var(--{name})" } }, // writes `var(--space-small)`
|
|
702
|
+
cover: { control: { type: "file", accept: "image/*" } }
|
|
703
|
+
}
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
`of` is a token's type or a part of its name. A prop asks for tokens from its TSDoc as well (`@token color`). The tokens
|
|
707
|
+
load when such a control is drawn; without any to offer it stays a text field.
|
|
708
|
+
|
|
709
|
+
Each value has three states, and the panel keeps them apart: the story's own (nothing in the address), the panel's (an
|
|
710
|
+
empty text included), and no value at all, where the component receives `undefined` over what the story's args give it.
|
|
711
|
+
Beside each field, one button leaves it with no value and another goes back to the story's; a cleared number has no
|
|
712
|
+
value. The address writes no value as `{"forgebookUnset":true}`.
|
|
713
|
+
|
|
714
|
+
Every value is plain JSON, kept in `args=`, text in any script included, and checked when a link is opened, in an embed
|
|
715
|
+
too: an object may hold only the fields its type declares. What JSON's fallback holds has no type to check against, so
|
|
716
|
+
what would run if a component spread the object on an element is left out (`onclick`, `innerHTML`, `srcdoc`, Vue's
|
|
717
|
+
`.innerHTML`, an address that starts with `javascript:`), as is a named value; `__proto__`, `constructor`, `prototype` and
|
|
718
|
+
values nested over 16 levels are not read at all. A token's value is a plain text, as a colour's is, and a link may give
|
|
719
|
+
another. A file is kept in the link as a `data:` address, so a `file` control takes about 6 KB, of a picture, sound, film,
|
|
720
|
+
plain text or a PDF. An object of exactly `of` and `named` reads as a named value. A very long value makes a very long
|
|
721
|
+
address, which some servers refuse, so the worst case leaves out a value too long for one.
|
|
722
|
+
|
|
723
|
+
A `Date` prop gets a date and time field, and the story receives a `Date`; a link writes it as `{"forgebookDate":"<ISO>"}`.
|
|
724
|
+
The `date` control a story names sets a number of ms instead, as Storybook's does.
|
|
725
|
+
|
|
726
|
+
The editors are one chunk that loads when the first such prop is drawn: a catalogue of plain props never asks for it.
|
|
727
|
+
|
|
728
|
+
### Controls of your own
|
|
729
|
+
|
|
730
|
+
A control the panel lacks is a module of yours, registered by name and loaded when a story names it. A catalogue that
|
|
731
|
+
registers none loads nothing for this.
|
|
732
|
+
|
|
733
|
+
```ts
|
|
734
|
+
// forgebook.config.ts
|
|
735
|
+
export default defineConfig({ /* … */ argControls: { rating: "./controls/rating.tsx" } });
|
|
736
|
+
|
|
737
|
+
// a story
|
|
738
|
+
argTypes: { score: { control: { type: "rating", settings: { stars: 7 } } } }
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
```tsx
|
|
742
|
+
// controls/rating.tsx
|
|
743
|
+
import type { ArgControlProps, ValueSchema } from "forgebook/arg-control";
|
|
744
|
+
import { createRoot } from "react-dom/client";
|
|
745
|
+
|
|
746
|
+
export const schema = { type: "integer", minimum: 0, maximum: 10 } satisfies ValueSchema;
|
|
747
|
+
|
|
748
|
+
export const mount = (element: HTMLElement, props: ArgControlProps) => {
|
|
749
|
+
const root = createRoot(element);
|
|
750
|
+
root.render(<Rating {...props} />);
|
|
751
|
+
return () => root.unmount();
|
|
752
|
+
};
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
`mount(element, { value, onChange, argType })` draws in the element and returns what takes it away. It is a function rather
|
|
756
|
+
than a component, so it can be written in any framework, whatever the stories' is: `createApp` in Vue, `mount` in Svelte,
|
|
757
|
+
plain DOM. `argType` holds the arg's `name`, `label` and `description`, and the control's `options`, `min`, `max`, `step`
|
|
758
|
+
and `settings`. The control keeps what it draws: it is mounted again only when the value changes from outside it.
|
|
759
|
+
|
|
760
|
+
`schema` says what the value looks like, in JSON Schema's words: `type`, `enum`, `const`, `minimum`, `maximum`, `minLength`,
|
|
761
|
+
`maxLength`, `pattern`, `items`, `minItems`, `maxItems`, `properties`, `required`, `additionalProperties`, `anyOf`. Any other
|
|
762
|
+
keyword is refused, since a schema that says less than it seems to would let a link through, as is a keyword holding what it
|
|
763
|
+
cannot use. A `pattern` has the `u` flag, and may not repeat a repeat (`(a+)+`), which could hang the page. The value is
|
|
764
|
+
always JSON, so a link keeps it and an agent sets it with no UI; what JSON cannot hold goes through named values, as
|
|
765
|
+
`{ of, named }`.
|
|
766
|
+
|
|
767
|
+
A link's value is checked against the schema before it reaches the story, and each `onChange` too. A control no one
|
|
768
|
+
registered, a module that fails to load, a `mount` that throws and a value that does not fit each give a sentence beside the
|
|
769
|
+
field; the other fields go on working. An error the control throws later, in its own event handlers, is out of reach: it is
|
|
770
|
+
the page's.
|
|
771
|
+
|
|
772
|
+
**A custom control runs in the shell's page, with a plugin's trust.** It can read and do whatever the catalogue's page can;
|
|
773
|
+
unlike a story, it has no origin of its own to keep it apart. Register only code you would run as a plugin. To be read by
|
|
774
|
+
agents its module also loads on the dev server when a command asks about a story that names it, for its `schema`: keep what
|
|
775
|
+
touches the page inside `mount`. A name that no control has (`control: "selct"`) is read as a custom control, and says so
|
|
776
|
+
beside the field.
|
|
777
|
+
|
|
477
778
|
## Docs pages
|
|
478
779
|
|
|
479
780
|
Guides, foundations and recipes are Markdown files with a little front matter, not MDX: a page is read at build time, and
|
|
@@ -503,27 +804,6 @@ The buttons read their colours from the page's theme.
|
|
|
503
804
|
```story acme:actions-button--primary
|
|
504
805
|
```
|
|
505
806
|
|
|
506
|
-
## Search
|
|
507
|
-
|
|
508
|
-
The field at the top of the sidebar searches the whole catalogue. The tree filters by name as you type, for browsing;
|
|
509
|
-
beside it a list ranks everything the words find, each component or page with what matched inside it and why (a prop,
|
|
510
|
-
an event, a heading, a sentence, a story), the words marked. `/` or ⌘K (Ctrl+K) comes to the field from anywhere (with the sidebar hidden, ⌘K alone,
|
|
511
|
-
which shows it), ↑ and ↓ move, Enter opens at the exact heading or prop, ⌘Enter or a modified press opens a new tab, Escape empties the field.
|
|
512
|
-
|
|
513
|
-
A prop has an address of its own, so a link to one can be pasted anywhere: `?component=acme:inputs-select#prop.value`,
|
|
514
|
-
and `#prop.Group.label` for a prop of a part named in `subcomponents`. A heading of a component's documentation file
|
|
515
|
-
has one too (`#accessibility`), as a docs page's has.
|
|
516
|
-
|
|
517
|
-
There is no search server and nothing to set up: the plugin builds the index with the catalogue, and the shell loads it
|
|
518
|
-
the first time someone types, so a reader who never searches pays nothing. A static build searches as the dev server
|
|
519
|
-
does. Only what the page does not already hold goes in the index: under 4 KB gzipped for a small library of ten components
|
|
520
|
-
with their documentation and a few guides. On a dev server, the first search waits for your packages' TypeScript for the props; names and docs
|
|
521
|
-
pages answer first.
|
|
522
|
-
|
|
523
|
-
Search and `forgebook find` rank alike, with the same synonyms: `modal` finds a `Dialog`, and a name counts more than a
|
|
524
|
-
sentence. A sentence copied from a page finds that page first. Change the words people see with `strings`, as any
|
|
525
|
-
other text of the shell (`"search.kindPage": "Guides"`).
|
|
526
|
-
|
|
527
807
|
## The props
|
|
528
808
|
|
|
529
809
|
```props acme:actions-button
|
|
@@ -647,7 +927,7 @@ docs page, with every block above, a few values and the generated home's pieces
|
|
|
647
927
|
|
|
648
928
|
````md
|
|
649
929
|
---
|
|
650
|
-
description: "{{components}} components
|
|
930
|
+
description: "{{components}} components and {{stories}} stories, version {{version}}."
|
|
651
931
|
---
|
|
652
932
|
|
|
653
933
|
Our design system, for every product team.
|
|
@@ -687,6 +967,31 @@ page of the catalogue's own `docs`; a library's page is linked by its address (`
|
|
|
687
967
|
the file are listed above the home and by `forgebook pages`, and `forgebook page home` reads it as an agent would. Restart
|
|
688
968
|
`forgebook dev` after adding or removing the file.
|
|
689
969
|
|
|
970
|
+
## Search
|
|
971
|
+
|
|
972
|
+
The field at the top of the sidebar searches the whole catalogue. The tree filters by name as you type, for browsing;
|
|
973
|
+
beside it a list ranks everything the words find, each component or page with what matched inside it and why (a prop,
|
|
974
|
+
an event, a heading, a sentence, a story), the words marked. `/` or ⌘K (Ctrl+K) comes to the field from anywhere (with the sidebar hidden, ⌘K alone,
|
|
975
|
+
which shows it), ↑ and ↓ move, Enter opens at the exact heading or prop, ⌘Enter or a modified press opens a new tab, Escape empties the field.
|
|
976
|
+
|
|
977
|
+
A prop has an address of its own, so a link to one can be pasted anywhere: `?component=acme:inputs-select#prop.value`,
|
|
978
|
+
and `#prop.Group.label` for a prop of a part named in `subcomponents`. A heading of a component's documentation file
|
|
979
|
+
has one too (`#accessibility`), as a docs page's has.
|
|
980
|
+
|
|
981
|
+
There is no search server and nothing to set up: the plugin builds the index with the catalogue, and the shell loads it
|
|
982
|
+
the first time someone types, so a reader who never searches pays nothing. A static build searches as the dev server
|
|
983
|
+
does. Only what the page does not already hold goes in the index: under 4 KB gzipped for a small library of ten components
|
|
984
|
+
with their documentation and a few guides. On a dev server, the first search waits for your packages' TypeScript for the props; names and docs
|
|
985
|
+
pages answer first.
|
|
986
|
+
|
|
987
|
+
Search and `forgebook find` rank alike, with the same synonyms: `modal` finds a `Dialog`, and a name counts more than a
|
|
988
|
+
sentence. A sentence copied from a page finds that page first. For `forgebook find`, `forgebook search`, the dev
|
|
989
|
+
server's API and MCP, a word nothing holds as typed is read as the nearest name, one letter away for a word of four
|
|
990
|
+
to seven letters and two from eight: `buton` finds `Button`, and the answer says what it read. A word that finds
|
|
991
|
+
something is never read as another, so a typo never changes an exact answer. The shell's search reads words as
|
|
992
|
+
typed: reading typos would cost more than its first search's weight allows. Change the words people see with `strings`, as any
|
|
993
|
+
other text of the shell (`"search.kindPage": "Guides"`).
|
|
994
|
+
|
|
690
995
|
## Design links
|
|
691
996
|
|
|
692
997
|
A component carries its own link to Figma, on its meta; a story with a design of its own writes it over the meta's:
|
|
@@ -724,13 +1029,82 @@ blocks it uses Open in Figma. A catalogue published with a Content Security Poli
|
|
|
724
1029
|
`forgebook story` and `get_story` return `design`: the link, the `fileKey` and `nodeId` a Figma tool takes, and the
|
|
725
1030
|
`address` of the Design tab to hand a person (it opens the story alone without the plugin). `list_stories` gives each component's, and `CATALOG.md` lists it.
|
|
726
1031
|
|
|
1032
|
+
## Stepping through a play
|
|
1033
|
+
|
|
1034
|
+
The Interactions tab lists a play's named steps and each action it took through the `canvas` and `userEvent` of its context,
|
|
1035
|
+
with its time and its failure. **Step through** mounts the story again and stops before each action and each `step`: **Next**
|
|
1036
|
+
does that one, **Continue** runs the rest. A `getBy…` query is logged but cannot stop, as it returns at once. What a play
|
|
1037
|
+
imports itself (`storybook/test`, `@testing-library/user-event`) runs unrecorded: use the context's tools to see it here.
|
|
1038
|
+
|
|
1039
|
+
## Plays when a story opens
|
|
1040
|
+
|
|
1041
|
+
A story's `play` runs when it opens on a dev server, where its author is checking it. In a build it waits: a reader of
|
|
1042
|
+
published docs did not ask for it, and the Interactions tab offers it with **Run**. A reader changes this for themself in
|
|
1043
|
+
that tab or the View menu (kept in their browser), an address says it for one link (`?play=off`, `?play=on`), and
|
|
1044
|
+
`play: "on"` or `"off"` in the configuration sets the default. Tests, `forgebook check` and `check_story` open a frame with
|
|
1045
|
+
no such word, so they run every play whatever a reader or a catalogue chose.
|
|
1046
|
+
|
|
1047
|
+
## Tags
|
|
1048
|
+
|
|
1049
|
+
Tag stories as CSF does, in the preview, a file's meta or a story; a `!name` takes one away:
|
|
1050
|
+
|
|
1051
|
+
```ts
|
|
1052
|
+
export default { component: Button, tags: ["beta"] } satisfies Meta<typeof Button>;
|
|
1053
|
+
|
|
1054
|
+
export const Experiment: StoryObj = { tags: ["!beta", "wip"] };
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
When any story has a tag of its own, the sidebar offers a filter beside the search: tick a tag to keep the stories that
|
|
1058
|
+
hold it, press its other button to leave them out. A story stays when it holds any tag asked for and none left out. The
|
|
1059
|
+
filter lives in the address (`?tag=beta&tag=!wip`), so a link keeps it, and it holds from page to page; a page opened by
|
|
1060
|
+
its address shows even if the filter leaves it out. `dev`, `test`, `autodocs`, `play-fn` and `test-fn` say what a tool
|
|
1061
|
+
does with a story, and are not offered. Agents ask the same way: `forgebook stories --tag beta --tag '!wip'`, or `tags`
|
|
1062
|
+
on `list_stories`. A tag can also be a [status](#statuses), shown as a badge.
|
|
1063
|
+
|
|
1064
|
+
## Statuses
|
|
1065
|
+
|
|
1066
|
+
A component's status (waiting for review, experimental, deprecated) is a tag the catalogue declares as a status. Write
|
|
1067
|
+
it on the file's meta for the component, or on a story for that story alone:
|
|
1068
|
+
|
|
1069
|
+
```ts
|
|
1070
|
+
export default { component: Card, tags: ["review"] } satisfies Meta<typeof Card>;
|
|
1071
|
+
|
|
1072
|
+
export const Flipped: StoryObj = { tags: ["experimental"] };
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
`review`, `experimental` and `deprecated` are declared by default. A catalogue restyles or renames one, declares its
|
|
1076
|
+
own, or turns one off with `false`; `tone` is `neutral`, `info`, `positive`, `caution` or `critical`:
|
|
1077
|
+
|
|
1078
|
+
```ts
|
|
1079
|
+
export default defineConfig({
|
|
1080
|
+
statuses: { review: { label: "In review" }, beta: { label: "Beta", tone: "positive" }, experimental: false }
|
|
1081
|
+
});
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
Once any story has a tag, a status shows as a badge in the component's sidebar entry and at the top of its page, and a
|
|
1085
|
+
story's own beside its example; a page without one looks as before, and a catalogue with no tags loads nothing for them.
|
|
1086
|
+
A component written in several files takes its first file's meta, as its docs do. The [tag filter](#tags) filters by a
|
|
1087
|
+
status as by any tag (`?tag=review`), and shows it as its badge. The default labels are shell texts
|
|
1088
|
+
(`strings: { "statuses.review": "In revisione" }`), and the `caution-text` and `positive-text` theme roles colour their
|
|
1089
|
+
tones. Storybook's status addons write `parameters.status` (`"beta"`, `{ type: "beta" }`): it is read as tags, so declare
|
|
1090
|
+
those statuses to show them.
|
|
1091
|
+
|
|
1092
|
+
In a catalogue with tags, a component deprecated in its TSDoc (`@deprecated`) shows the `deprecated` badge on its page,
|
|
1093
|
+
with the reason in its tooltip, even without the tag. The sidebar reads tags only, so `forgebook story` and `get_story`
|
|
1094
|
+
name the component whose TSDoc and meta disagree: tag the meta so the sidebar and the filter say it, or write
|
|
1095
|
+
`@deprecated` so `check-code` warns where it is imported. Agents read `statuses` on each component and story of
|
|
1096
|
+
`forgebook stories --json`, `list_stories`, `forgebook story --json` and `get_story`, and filter with `--tag review` or
|
|
1097
|
+
`tags`.
|
|
1098
|
+
|
|
727
1099
|
## Coming from Storybook
|
|
728
1100
|
|
|
729
1101
|
Forgebook reads Storybook's story files as they are: CSF 3 and CSF 2 (`Template.bind({})`), titles of any depth and
|
|
730
1102
|
titles from the file's path, `args` and `argTypes` (with their controls), actions from `fn()` or an `argTypes` action,
|
|
731
1103
|
`play` with Testing Library (`@testing-library/dom` and `@testing-library/user-event`, optional peers), `loaders`,
|
|
732
|
-
`tags` (`!dev`, `!test
|
|
733
|
-
`.
|
|
1104
|
+
`tags` (`!dev`, `!test`, and your own for the [tag filter](#tags)), a story's `globals`, JSX in `.js` files, stories and components alike (Vite compiles JSX only in `.jsx` and
|
|
1105
|
+
`.tsx`; with React, Preact or Next.js, Forgebook compiles the catalogue's own `.js` files that hold JSX itself, with the
|
|
1106
|
+
project's JSX options, `jsxInject` and Fast Refresh, and leaves installed packages, the workspace's other packages and
|
|
1107
|
+
`?raw`, `?url` or `?worker` imports alone; a project that sets `oxc: false` compiles its scripts its own way),
|
|
734
1108
|
hooks in a `render` or a decorator (React and
|
|
735
1109
|
Preact, with the story's state kept when a decorator draws `<Story />`), Storybook's preview hooks from
|
|
736
1110
|
`storybook/preview-api` in a React story file (`useArgs`, whose update and reset change the story's args as the panel
|
|
@@ -747,6 +1121,14 @@ cannot load (it uses one so, or one of its own files reads `document`) leaves th
|
|
|
747
1121
|
answers working: it is left out of them and named among their problems, with what it used. A
|
|
748
1122
|
`**` pattern skips `node_modules` unless it names it.
|
|
749
1123
|
|
|
1124
|
+
A link keeps working too: `args=` reads Storybook's form (`label:Hello+world;count:5;open:!true;tags[0]:a;tags[1]:b`,
|
|
1125
|
+
with `!null`, `!hex(…)`, `!rgb(…)`, `!hsl(…)` and `!date(…)`) as well as Forgebook's own JSON, told apart by JSON's first
|
|
1126
|
+
`{`. Its values are typed by the prop that receives them, so `5` is a number for a number and stays `5` for a text, and they
|
|
1127
|
+
meet the same check as any link's. Forgebook writes its own form once a value changes in the panel. What an arg here
|
|
1128
|
+
cannot hold (an object such as `style.color:red`, a list with a gap) is named above the story, not dropped
|
|
1129
|
+
in silence. An arg's `argTypes.<name>.if` (`{ arg: "kind", eq: "link" }`, `{ global: "theme", neq: "dark" }`, with `truthy`,
|
|
1130
|
+
`exists`, `eq` or `neq`) hides its control the same way Storybook's does, and leaves its value alone.
|
|
1131
|
+
|
|
750
1132
|
It lists what Storybook's indexer lists:
|
|
751
1133
|
|
|
752
1134
|
- every named export as a story, whatever writes its value: an object, a function, a helper's result
|
|
@@ -773,6 +1155,8 @@ and whose root holds the stories, with the paths to the packages' sources, gets
|
|
|
773
1155
|
It does not read MDX, doc blocks or addons. Actions are explicit only: an `on*` prop is not turned into an action by
|
|
774
1156
|
its name.
|
|
775
1157
|
|
|
1158
|
+
### From Storybook
|
|
1159
|
+
|
|
776
1160
|
To try it on a Storybook project, run `npx forgebook init --from storybook` in the folder that holds `.storybook`. It
|
|
777
1161
|
writes `forgebook.config.ts`: the story globs of `main` (the MDX pages left out), the preview as Forgebook's preview,
|
|
778
1162
|
and a switch for each toolbar global of the preview, applied by code as its decorators expect. `main` is loaded, so a
|
|
@@ -834,6 +1218,41 @@ A monorepo that points its packages at their sources with an export condition (`
|
|
|
834
1218
|
`forgebook` command: its dev server reads the stories and previews it answers tools with on Node under the same
|
|
835
1219
|
conditions, those of other runtimes (`browser`) aside, unless a plugin names the server's own.
|
|
836
1220
|
|
|
1221
|
+
## Other catalogues, already published
|
|
1222
|
+
|
|
1223
|
+
A catalogue can list another one, built and deployed elsewhere, after its own stories: a product's catalogue showing the
|
|
1224
|
+
design system it uses, from the design system's own site. Each is named in `refs`, by an id of lower-case words:
|
|
1225
|
+
|
|
1226
|
+
```ts
|
|
1227
|
+
export default defineConfig({
|
|
1228
|
+
framework: react(),
|
|
1229
|
+
title: "Checkout",
|
|
1230
|
+
stories: ["src/**/*.stories.tsx"],
|
|
1231
|
+
refs: {
|
|
1232
|
+
"design-system": { title: "Acme design system", url: "https://design.acme.dev/" }
|
|
1233
|
+
}
|
|
1234
|
+
});
|
|
1235
|
+
```
|
|
1236
|
+
|
|
1237
|
+
- **What the other catalogue needs.** It is built with `forgebook build --agent-index`, so `forgebook-agent.json` sits
|
|
1238
|
+
beside its pages, and its host lets this catalogue fetch that file (an `Access-Control-Allow-Origin` header) and frame
|
|
1239
|
+
its page (no `frame-ancestors` that leaves this catalogue out). Without the index its section says so, and why.
|
|
1240
|
+
- **Lazy.** Its section starts shut, and its index is fetched only when it is first opened, or when an address shows one
|
|
1241
|
+
of its stories. A catalogue without `refs` loads none of this code.
|
|
1242
|
+
- **Read-only, on its own origin.** A story is drawn by the other catalogue's own embed page (`?embed`), with its own
|
|
1243
|
+
preview and theme, in a frame on its origin, sandboxed as a story's frame is and given only the browser features that
|
|
1244
|
+
ask the reader nothing (full screen, video, writing to the clipboard). Nothing it runs reaches this page, and this page
|
|
1245
|
+
sends it nothing but an address: its controls, panel and docs are in its own page, one click away (*Open in …*). One
|
|
1246
|
+
served on the origin this catalogue's stories run on is refused.
|
|
1247
|
+
- **The toolbar's switches cross.** Every switch of this catalogue goes in the address, its value included when it is
|
|
1248
|
+
the default; the other catalogue keeps those it declares, with values it knows (the theme of both, say), and drops the
|
|
1249
|
+
rest. The width, the device and the panel's values do not cross.
|
|
1250
|
+
- **The address** of one of its stories is `?ref=<id>&story=<its id>`, so it can be shared like any other; a `ref` that
|
|
1251
|
+
names none of them (`?ref=newsletter`, another site's mark) leaves the story this catalogue's. No switch may be named
|
|
1252
|
+
`ref`.
|
|
1253
|
+
- **Not searched.** The search and the tag filter read this catalogue only: searching another would fetch its whole index
|
|
1254
|
+
at the first key. Agents read a published catalogue on its own with `forgebook mcp --from <its address>`.
|
|
1255
|
+
|
|
837
1256
|
## A lighter catalogue
|
|
838
1257
|
|
|
839
1258
|
Two features cost the most and are on by default: the accessibility check, which ships axe (about 180 KB gzipped in a
|
|
@@ -863,17 +1282,78 @@ export default defineConfig({
|
|
|
863
1282
|
with no `docs`, and `forgebook changed` says it finds no story through the imports. `forgebook docs` and `forgebook new`
|
|
864
1283
|
still read the types when run, since they are asked for that. `usedIn: true` with it is an error.
|
|
865
1284
|
|
|
1285
|
+
## When a configuration is refused
|
|
1286
|
+
|
|
1287
|
+
Everything below stops every command with an exit code of 1 and a message that names the mistake, and a Vite build with
|
|
1288
|
+
the plugin too, since each would otherwise give a catalogue that looks right and is not:
|
|
1289
|
+
|
|
1290
|
+
- **A key nothing reads.** `toolbr: […]` says `it has no key "toolbr". Did you mean toolbar?`; a preset's keys and those of
|
|
1291
|
+
`build`, `shell`, `a11y`, `backgrounds`, `widthRange` and `controls` are checked too. An adapter's and a plugin's options
|
|
1292
|
+
are their own functions' arguments, so nothing adds a key to the configuration.
|
|
1293
|
+
- **A toolbar, a key or a view the shell cannot use**, as [the toolbar](#the-toolbar) says.
|
|
1294
|
+
- **A file that does not load.** A syntax error is named as `/path/forgebook.config.ts:3:3: …`, in the configuration or in a
|
|
1295
|
+
TypeScript file it imports. In a JavaScript file it imports, Node gives no place, and the message names the configuration.
|
|
1296
|
+
- **A story pattern that matches nothing**, for `forgebook build`, `stories` and `check`: each pattern is read on its own,
|
|
1297
|
+
so a mistaken one beside one that works is named as well, and what a `!` pattern leaves out does not count. `verify`
|
|
1298
|
+
answers `not-verified` with the pattern in `skipped` when the change touches a UI file, and still gives the rest of its
|
|
1299
|
+
answer (what changed, the stories that ran, what they found), so the empty pattern never hides a failure. A change with
|
|
1300
|
+
no UI file, such as the commit of `init`'s own files before a first story, is `nothing-to-verify`, with the pattern and
|
|
1301
|
+
how to write the first story in `skipped`.
|
|
1302
|
+
`forgebook dev` warns instead and opens a home that says where it looks, since you may be about to write the first story;
|
|
1303
|
+
saving a file the patterns match shows it. A source's own patterns are its module's to read, and a list of `docs` globs
|
|
1304
|
+
may match nothing.
|
|
1305
|
+
- **Story files in a hidden folder**: a pattern's `**` does not enter a folder whose name starts with a dot
|
|
1306
|
+
(`docs/.vitepress`). Files a pattern would match there are named, with the pattern that lists them
|
|
1307
|
+
(`docs/.vitepress/**/*.stories.ts`): a warning from `forgebook dev`, a problem in `stories`, and the hint when a pattern
|
|
1308
|
+
matches nothing. Nothing stops on them; a `!` pattern for the folder (`!docs/.vitepress/**`) says it is left out on
|
|
1309
|
+
purpose and ends the notice.
|
|
1310
|
+
|
|
1311
|
+
## Stories that load
|
|
1312
|
+
|
|
1313
|
+
A frame says a story is ready only once what the story loads is in the page, so `check`, `verify`, `shot`, the generated
|
|
1314
|
+
tests and the compare view read the content and never a fallback. A play function starts after the same wait, and the
|
|
1315
|
+
frame waits again after it, for what its clicks load. For React (Next.js and React Native too) and Vue (Nuxt too), that
|
|
1316
|
+
is every `Suspense` boundary, whoever wrote it, however deep; in React also a transition or a deferred value still
|
|
1317
|
+
loading, which shows no fallback.
|
|
1318
|
+
|
|
1319
|
+
The wait is bounded: 10 s by default. Past it the frame's status is `pending`, with the reason on the page and in
|
|
1320
|
+
`data-forgebook-pending-reason`. `check` reports the story `notVerified` and exits non-zero, `verify` answers
|
|
1321
|
+
`not-verified`, `shot` marks the picture `pending`, the generated test fails and the Accessibility card does not pass it:
|
|
1322
|
+
what shows is not what you meant to check. Say so when a story is meant to stay there, or give it longer:
|
|
1323
|
+
|
|
1324
|
+
```tsx
|
|
1325
|
+
export const Loading = { parameters: { suspense: { expected: true } } }; // a loading state: ready at once
|
|
1326
|
+
export const Slow = { parameters: { suspense: { timeout: 15_000 } } }; // in ms; in the preview, for every story
|
|
1327
|
+
```
|
|
1328
|
+
|
|
1329
|
+
`check` stops listening to a frame at 30 s, and a generated test at 25 s: past that a longer `timeout` fails as a frame
|
|
1330
|
+
that never answered, never as a pass. A story with `expected` is checked as it first drew, and `verify` names it in
|
|
1331
|
+
`skipped`, so a preview cannot turn the wait off unseen.
|
|
1332
|
+
|
|
1333
|
+
React and Vue do not say what is pending in public, so the adapters read their private tree. Each page first tries that
|
|
1334
|
+
reading on a boundary known to be pending and on one known not to be; on a version of React or Vue where it no longer
|
|
1335
|
+
works, every story is `pending` with that reason instead of ready on a guess. A cost of about one tree walk per story,
|
|
1336
|
+
in the adapter. A root a story creates itself with `createRoot` is out of reach, and so is what a story loads without
|
|
1337
|
+
`Suspense` (a `useEffect` fetch with a spinner): a play function can wait for that with `findBy…`.
|
|
1338
|
+
|
|
1339
|
+
The other adapters have no such wait. Angular's `drawn` waits for the application to be stable. Preact's
|
|
1340
|
+
`preact/compat` `Suspense` and Solid's `<Suspense>` are not read; a story using them is ready once drawn, so its play
|
|
1341
|
+
function has to wait for the content. Svelte, Web Components and plain HTML have no fallback to read (Svelte's `{#await}`
|
|
1342
|
+
is a state of the component: the same).
|
|
1343
|
+
|
|
866
1344
|
## Commands
|
|
867
1345
|
|
|
868
1346
|
| Command | What it does |
|
|
869
1347
|
|---|---|
|
|
870
|
-
| `forgebook dev` / `forgebook build` | Runs the catalogue of `forgebook.config.ts`, or builds it into static files |
|
|
1348
|
+
| `forgebook dev` / `forgebook build` | Runs the catalogue of `forgebook.config.ts`, or builds it into static files; see [a story file that cannot be read](#a-story-file-that-cannot-be-read) |
|
|
1349
|
+
| `forgebook build --watch` | Builds again when a file the catalogue reads changes; see [building on every change](#building-on-every-change) |
|
|
871
1350
|
| `forgebook prebundle` | Pre-bundles the catalogue's dependencies, as `dev` does on a first start: before it, in CI or an image |
|
|
872
1351
|
| `forgebook stories` | Lists every story with its id and links; fails when the story index has problems |
|
|
873
1352
|
| `forgebook story <id>` | One story: its args, its component's props, its links in a view (`--view theme=dark`) |
|
|
874
|
-
| `forgebook init
|
|
875
|
-
| `forgebook new <file>` | Writes a component's first stories from its props: the main story, one per choice, the worst case |
|
|
1353
|
+
| `forgebook init` | Writes `forgebook.config.ts`, and a preview with the app's global style sheets, from the project's framework; names what it could not work out; `--install` installs, `--json` answers for agents ([more](#start-with-one-command)). `--from storybook` reads a project's `.storybook` (or on-device `.rnstorybook`) folder instead: framework, story globs, preview and toolbar globals |
|
|
1354
|
+
| `forgebook new <file>` | Writes a component's first stories from its props and from where the application draws it: the main story with the values the application gives most, one story per other way it is drawn, one per choice, the worst case; the file says which props it was written for, and `verify` fails when they change. `--repair` draws them in a browser and, when an error says a story must be drawn inside a component the application exports (`useSidebar must be used within a SidebarProvider`), draws them inside it, at most twice |
|
|
876
1355
|
| `forgebook check <id>` | Runs the catalogue's browser checks on a story (or a component's stories): accessibility, spills, overlays, errors; with `@forgebook/testing` |
|
|
1356
|
+
| `forgebook check <id> --stress` | Draws the story in the states nobody wrote: long text, unbreakable words, huge numbers, empty, right to left, 200 % zoom, narrow, dark, more contrast, and reports what each breaks |
|
|
877
1357
|
| `forgebook errors` | Lists what the open frames reported while you edited (a story that could not be shown, a failed play); `--follow` keeps listing |
|
|
878
1358
|
| `forgebook shot <id>` | Takes a story's pictures, one PNG per view, at the view's width and the story's height (framed in a device a view names); with `@forgebook/testing` |
|
|
879
1359
|
| `forgebook pages` | Lists the docs pages with their ids and addresses; fails when the index has problems |
|
|
@@ -882,17 +1362,48 @@ export default defineConfig({
|
|
|
882
1362
|
| `forgebook mcp` | Serves the same answers to an agent over the Model Context Protocol |
|
|
883
1363
|
| `forgebook check-page` | Runs the checks on a real page of the application, by its address |
|
|
884
1364
|
| `forgebook changed` | Lists the stories a change touches, through what they import |
|
|
885
|
-
| `forgebook verify` |
|
|
1365
|
+
| `forgebook verify` | Says whether a change is done: every changed UI file in stories that ran, in every variant, and `check-code`, with one exit code |
|
|
1366
|
+
| `forgebook proof` | Compares a change's states with where it started, both drawn in one run, for a pull request's comment |
|
|
1367
|
+
| `forgebook visibility` | Lists the published components an agent cannot see, with the fix for each |
|
|
886
1368
|
| `forgebook find` | Finds components by what they are for, in your own words |
|
|
887
1369
|
| `forgebook search` | Searches the whole catalogue by its text: docs pages, props, descriptions, stories and names, with the address of each match |
|
|
1370
|
+
| `forgebook graph` | A map of the components and tokens, generated from the source: what exists, what each is for, how to choose |
|
|
1371
|
+
| `forgebook audit` | The colours, sizes and components a project actually uses, and the components with no story or no use, with no catalogue |
|
|
888
1372
|
| `forgebook tokens` | The design tokens, one of them, or the token nearest a value (`--nearest "#1c1b1b"`) |
|
|
889
1373
|
| `forgebook check-code` | Checks what a change adds against the design system, with an exit code for CI |
|
|
890
|
-
| `forgebook agents` |
|
|
1374
|
+
| `forgebook init --agents` | What a project's coding agents need too: `.mcp.json`, instructions and skill, CI, a hook running `verify`; `--browser` installs the testing package and its browser, `--draw` draws a story ([more](#set-a-project-up-in-one-prompt)) |
|
|
1375
|
+
| `forgebook hook <assistant>` | What an assistant's hook runs when it is about to finish: `verify` on the change, sending the agent back on red |
|
|
1376
|
+
| `forgebook browser` | Installs the browser the checks run on, with the Playwright that will open it, whatever other Playwright the project has; it adds that one build and never removes another project's; `--with-deps` also installs what it needs of the system |
|
|
1377
|
+
| `forgebook agents` | Adds Forgebook's short instructions to the project's `AGENTS.md` (and an [Agent Skill](https://agentskills.io) with `--skill`, in `.agents/skills` and `.claude/skills`) |
|
|
1378
|
+
| `forgebook guide` | Prints the rest of the instructions on demand: a framework's or a topic's full text |
|
|
891
1379
|
| `forgebook docs` | Writes a package's `CATALOG.md` and `llms.txt` from its exports, their TSDoc and its stories |
|
|
1380
|
+
| `forgebook migrate` | Rewrites a project for a breaking change: lists the changes (exit 1 while unwritten), writes them with `--write` ([more](#upgrading)) |
|
|
892
1381
|
| `forgebook-texts` | Checks a package's story texts and lists what each language still has to translate |
|
|
893
1382
|
|
|
894
1383
|
Each command prints its options with `--help`; `stories` and `story` print JSON with `--json`.
|
|
895
1384
|
|
|
1385
|
+
### A story file that cannot be read
|
|
1386
|
+
|
|
1387
|
+
On a dev server, a story file that does not parse, or that has no meta, is one problem and the rest of the catalogue
|
|
1388
|
+
keeps drawing. The problem names the file with its line and column, on the page, in `forgebook stories`, `forgebook
|
|
1389
|
+
errors` and their MCP tools. Saving the fix is enough, with no restart: a story already drawn stays drawn while its file
|
|
1390
|
+
is broken, and redraws in place. `forgebook build` and a test run still fail on it: a build ends with
|
|
1391
|
+
`src/Bad.stories.tsx:4:45 does not parse: …` and a non-zero exit, and only `--verbose` adds the bundler's stack.
|
|
1392
|
+
|
|
1393
|
+
A component, or any other of the project's scripts, that does not parse is one problem too, named with its line and
|
|
1394
|
+
column on the page, in `forgebook stories`, `forgebook errors` and their MCP tools, before any frame loads it; the
|
|
1395
|
+
stories that import it say why in their frame and in `forgebook check`, and the others draw. Saving the fix takes the
|
|
1396
|
+
problem away. Neither it nor a story file stops Vite's dependency scan on a first start: the packages it imports are
|
|
1397
|
+
still pre-bundled with the others', and `forgebook prebundle` prints one line per such script
|
|
1398
|
+
(`The dependency scan skipped src/Broken.tsx:3:24 does not parse: Unexpected token`). What a scan found is kept for
|
|
1399
|
+
later starts; a script broken while no server ran is named once it is edited, or by the frames that import it.
|
|
1400
|
+
|
|
1401
|
+
A story that loads but cannot be drawn says why in its frame and in `forgebook check`. On the dev server, the stack under
|
|
1402
|
+
the reason gives the lines of the files as written: the frame sends it, and the server maps it through the source maps of
|
|
1403
|
+
the modules it served, so the frame loads no map. `forgebook errors` and `recent_errors` give the error's `place`, the
|
|
1404
|
+
first of those lines outside `node_modules` (`src/Button.stories.tsx:3:11`). A static build's stack keeps the lines of the
|
|
1405
|
+
built code.
|
|
1406
|
+
|
|
896
1407
|
`forgebook build` names each script it writes by its hash alone (`assets/3f9a1c2b.js`): a catalogue names every story
|
|
897
1408
|
file's scripts once per loader, so shorter names make a smaller build. To read what a build holds, name each script after
|
|
898
1409
|
its module too (`assets/Button-3f9a1c2b.js`), for one build with `forgebook build --readable-names`, or for every build in
|
|
@@ -902,9 +1413,10 @@ the configuration:
|
|
|
902
1413
|
export default defineConfig({ /* … */ build: { readableNames: true } });
|
|
903
1414
|
```
|
|
904
1415
|
|
|
905
|
-
A component's docs, a story file's code and a component's page are data, not scripts: a build writes
|
|
906
|
-
`.bin`
|
|
907
|
-
(a story file whose stories are all args
|
|
1416
|
+
A component's docs, a story file's code and a component's page are data, not scripts: a build writes them gzipped in
|
|
1417
|
+
`.bin` files, neighbouring texts together up to 64 KB of JSON each so they compress as one, which the page unpacks when
|
|
1418
|
+
it shows one; a text no longer than its loader goes in the story index itself (a story file whose stories are all args
|
|
1419
|
+
has no code).
|
|
908
1420
|
|
|
909
1421
|
## Mocking an API
|
|
910
1422
|
|
|
@@ -960,39 +1472,109 @@ say what a catalogue has.
|
|
|
960
1472
|
|
|
961
1473
|
The contract, with how to write one and what it leaves out, is [PLUGINS.md](https://github.com/ensiria/forgebook/blob/develop/packages/forgebook/PLUGINS.md).
|
|
962
1474
|
|
|
1475
|
+
## The states nobody wrote a story for
|
|
1476
|
+
|
|
1477
|
+
`forgebook check <id> --stress` draws a story, then draws it again in each case below, with no story written and nothing
|
|
1478
|
+
saved to the project. Each case lists only what it finds that the plain story does not, so a problem the story already
|
|
1479
|
+
has is not blamed on the case. `check_story` with `stress: true` does the same for an agent.
|
|
1480
|
+
|
|
1481
|
+
| Case | What it gives the story |
|
|
1482
|
+
|---|---|
|
|
1483
|
+
| `long-text`, `unbreakable`, `empty` | every text prop: a long text, a word with no place to break, nothing |
|
|
1484
|
+
| `huge-number` | every number prop: its highest value, or 123,456,789,012 |
|
|
1485
|
+
| `rtl` | text in a right-to-left script, drawn right to left |
|
|
1486
|
+
| `zoom-200` | the story laid out at half the window's width, as a browser at 200 % lays it out |
|
|
1487
|
+
| `narrow` | long text at 320 px |
|
|
1488
|
+
| `dark`, `contrast` | the catalogue's dark scheme and more contrast, when it has them |
|
|
1489
|
+
|
|
1490
|
+
Values reach the story from a page on the catalogue's origin, as the props panel gives them, since a frame takes no values
|
|
1491
|
+
from its address. A case with nothing to set (no text prop, no dark scheme) is in `skipped`, never counted as passed.
|
|
1492
|
+
Overlays and focus order are left to `forgebook check`: no case changes them. A text prop that names something rather
|
|
1493
|
+
than shows it (an id, a class, an address, an icon's slot) is left alone, and in `empty` a missing accessible name is the
|
|
1494
|
+
value's doing and not reported. A frame that does not answer the page (its catalogue set on another origin with
|
|
1495
|
+
`frames.origins`) is reported as not judged, never as passed. It exits non-zero when a case adds a problem or cannot be
|
|
1496
|
+
judged; `--stress` takes no `--view`, `--all-variants` or `--semantic`.
|
|
1497
|
+
|
|
1498
|
+
## Building on every change
|
|
1499
|
+
|
|
1500
|
+
`forgebook build --watch` stays running and builds again when a file the catalogue reads changes, a new story file or
|
|
1501
|
+
docs page included, for a folder a host, an embedding app or another machine serves. A failed build says why on one line,
|
|
1502
|
+
leaves the last good output and waits for the next edit. A rebuild takes about two thirds of a whole `forgebook build`
|
|
1503
|
+
(2.2 to 2.9 s against 3 to 4 s on 5000 stories) and writes the whole folder again; the process holds about 2 GB there. Use
|
|
1504
|
+
`forgebook dev` to work on stories: it redraws in place.
|
|
1505
|
+
|
|
963
1506
|
## A preview for every pull request
|
|
1507
|
+
|
|
964
1508
|
`forgebook build` writes static files, so any static host gives each pull request its own catalogue, for free. `--base` is
|
|
965
1509
|
for a host that serves it under a sub-path. A host that sets a `Content-Security-Policy` needs `blob:` in `script-src` for
|
|
966
1510
|
the Accessibility card: a build keeps axe gzipped, and the frame runs it from a blob once unpacked. It also needs the
|
|
967
1511
|
catalogue's own origin in `connect-src` (`'self'`): the page fetches the docs, code, pages and "Used in" a build keeps
|
|
968
1512
|
gzipped. This workflow builds on
|
|
969
1513
|
every pull request and keeps the result as an artefact; the last step is yours, and any host works:
|
|
1514
|
+
|
|
970
1515
|
```yaml
|
|
971
1516
|
# .github/workflows/catalogue-preview.yml
|
|
972
1517
|
name: catalogue preview
|
|
973
1518
|
on:
|
|
974
1519
|
pull_request:
|
|
1520
|
+
|
|
975
1521
|
permissions:
|
|
976
1522
|
contents: read
|
|
1523
|
+
|
|
977
1524
|
jobs:
|
|
978
1525
|
preview:
|
|
979
1526
|
runs-on: ubuntu-latest
|
|
980
1527
|
steps:
|
|
981
|
-
- uses: actions/checkout@
|
|
982
|
-
- uses: actions/setup-node@
|
|
1528
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
1529
|
+
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
|
|
983
1530
|
with:
|
|
984
1531
|
node-version: 24
|
|
985
1532
|
cache: npm
|
|
986
1533
|
- run: npm ci
|
|
987
1534
|
- run: npx forgebook build --out preview --base "/pr-${{ github.event.number }}/"
|
|
988
|
-
- uses: actions/upload-artifact@
|
|
1535
|
+
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
1536
|
+
with:
|
|
989
1537
|
name: catalogue-preview
|
|
990
1538
|
path: preview
|
|
991
1539
|
# then publish the folder, for example:
|
|
992
1540
|
# GitHub Pages: actions/upload-pages-artifact, then actions/deploy-pages (one site, so without --base per PR)
|
|
993
1541
|
# Cloudflare: npx wrangler pages deploy preview --branch "pr-${{ github.event.number }}"
|
|
994
1542
|
# Your server: rsync -az preview/ "user@host:/var/www/previews/pr-${{ github.event.number }}/"
|
|
1543
|
+
```
|
|
1544
|
+
|
|
995
1545
|
A pull request from a fork gets no secrets, so a step that deploys only works for branches of your own repository.
|
|
1546
|
+
|
|
1547
|
+
## Proof in the pull request, made by CI
|
|
1548
|
+
|
|
1549
|
+
`forgebook proof --base origin/main` draws the states a change touches twice in one run, from the commit it is compared with and
|
|
1550
|
+
with the change, and writes `comment.md`, `proof.json`, `index.html` and the pictures. `forgebook proof
|
|
1551
|
+
--write-workflow` writes a GitHub workflow that posts that comment, one per pull request, replaced on each push. Nothing runs
|
|
1552
|
+
outside GitHub, and the pictures are the run's artefact.
|
|
1553
|
+
|
|
1554
|
+
- **No baseline, so nothing for an agent to approve.** The "before" is drawn in this run from where the change left the base (on a pull request's run, the base's tip), in a checkout
|
|
1555
|
+
with its own install and server, and thrown away. `forgebook-baselines` is never read, and `proof` has no `--update`.
|
|
1556
|
+
- **What differs is pictured again.** A state that also differs between two pictures of the same code is `unstable`, and its
|
|
1557
|
+
difference proves nothing. `forgebook proof --base HEAD --all` shows whether a project's pictures hold still between two
|
|
1558
|
+
builds of one commit.
|
|
1559
|
+
- **It says what it did not check:** what `verify` skipped, UI files no story reaches, stories past `--max-stories`, a state
|
|
1560
|
+
that did not draw, and real pages, which no story shows.
|
|
1561
|
+
- **A changed picture is for a person to judge.** The command exits zero once it has a proof, unless `--fail-on-verdict`
|
|
1562
|
+
holds it to `verify`'s verdict, as the workflow does.
|
|
1563
|
+
- **It cannot defend against the code it runs.** The branch's code runs in the comparing job, and a `postinstall` before it,
|
|
1564
|
+
so a hostile branch can forge the result or edit the workflow: no baseline can be approved, but a result can be faked. So
|
|
1565
|
+
that job holds no write token and keeps no credential. The posting job runs nothing of the branch, writes the comment
|
|
1566
|
+
itself from `proof.json` with every name in a code span or a fence, and lists from GitHub which files shape the run
|
|
1567
|
+
(`.github/`, `patches/`, `package.json`, lockfiles, `.npmrc`, the catalogue's configuration and preview). The comment says
|
|
1568
|
+
this at its top. Protect `.github/` with code owners if agents push to your repository, and treat `index.html` in a
|
|
1569
|
+
stranger's artefact as any download.
|
|
1570
|
+
- **A fork's pull request gets no comment.** GitHub gives its run a read-only token, so posting fails and the job goes on:
|
|
1571
|
+
the proof stays in the run's summary and its artefact. Never run this on `pull_request_target`, which would hand a fork's
|
|
1572
|
+
code a write token.
|
|
1573
|
+
- **A picture needs a download.** GitHub cannot show an artefact's picture in a comment, so the comment links the artefact:
|
|
1574
|
+
open `index.html` in it.
|
|
1575
|
+
- **A picture with something from the network proves little.** A story loading a random image reads as `unstable`, or as
|
|
1576
|
+
changed when it differs only between the two sides. Mock what a story fetches.
|
|
1577
|
+
|
|
996
1578
|
## Stories on an origin of their own
|
|
997
1579
|
|
|
998
1580
|
A story's code, or a value a shared link puts in one of its props (an `href`, an `html`), runs in its frame. So that it
|
|
@@ -1018,6 +1600,10 @@ catalogue as before; `forgebook dev --frames same-origin` does the same for one
|
|
|
1018
1600
|
worker, the clipboard, full screen, the camera and the microphone. A story's link can no longer take the catalogue's
|
|
1019
1601
|
tab to another address.
|
|
1020
1602
|
|
|
1603
|
+
A story's style sheets never leave its frame, on any origin: every story draws in a document of its own, so a story
|
|
1604
|
+
file that styles `body` or every `button` leaves the next story, the catalogue's page and the other frames of a docs page
|
|
1605
|
+
as they were, on the dev server and in a static build. What one origin changes is scripts, as above.
|
|
1606
|
+
|
|
1021
1607
|
What stays reachable from a frame: the code it runs (your stories, their components, what the catalogue imports), the
|
|
1022
1608
|
errors Vite sends every page while you edit, and `forgebook errors`, where a frame reports its own errors. If the
|
|
1023
1609
|
default port is taken, the frames move to the next free one, so their storage moves with them; give `--frame-port` to
|
|
@@ -1049,8 +1635,10 @@ A command, an agent or `curl` sends no browser headers: `forgebook mcp`, `forgeb
|
|
|
1049
1635
|
network, so use it only on one you trust.
|
|
1050
1636
|
|
|
1051
1637
|
## Your look in the shell
|
|
1638
|
+
|
|
1052
1639
|
The catalogue's page can take your library's colours, fonts and corners. Nothing runs in the browser for it: the theme is
|
|
1053
1640
|
written into the page at build, and the frames are left alone, since they belong to your stories.
|
|
1641
|
+
|
|
1054
1642
|
```ts
|
|
1055
1643
|
// forgebook.config.ts
|
|
1056
1644
|
shell: {
|
|
@@ -1059,16 +1647,31 @@ shell: {
|
|
|
1059
1647
|
dark: { accent: "#b28bff" },
|
|
1060
1648
|
fonts: { sans: "Inter, sans-serif" },
|
|
1061
1649
|
radius: { control: "4px", card: "8px" }
|
|
1650
|
+
}
|
|
1062
1651
|
}
|
|
1652
|
+
```
|
|
1653
|
+
|
|
1654
|
+
A library with design tokens (`tokens` in the configuration) can theme the shell from them in a few lines. Each role takes the
|
|
1655
|
+
token's value in both appearances, its `light` and `dark` themes first; colours written by hand win, and a token that
|
|
1656
|
+
is not there or is no colour is warned about and leaves the shell's own. The theme is read when the configuration is, so
|
|
1657
|
+
restart after editing the tokens.
|
|
1658
|
+
|
|
1659
|
+
```ts
|
|
1660
|
+
shell: { theme: { fromTokens: { accent: "color.brand.primary", surface: "color.background.card" } } }
|
|
1661
|
+
```
|
|
1662
|
+
|
|
1063
1663
|
Every role is optional, and typed by `ShellTheme` (`import type { ShellTheme } from "forgebook/shell-theme"`): the grounds
|
|
1064
1664
|
`window`, `sidebar`, `surface`; `separator`, `hover`; the texts `text`, `text-secondary`, `text-tertiary`, `accent-text`,
|
|
1065
|
-
`danger-text
|
|
1665
|
+
`danger-text`, `caution-text`, `positive-text` (the last two colour the [status badges](#statuses)); `accent`; and the `code-*` colours. Renaming or removing a role is a breaking change. A preset can give a
|
|
1066
1666
|
theme too, and the catalogue's wins role by role.
|
|
1067
|
-
|
|
1667
|
+
|
|
1668
|
+
The reader's Interface menu also offers **Follow the story**, where the catalogue has a theme switch: a dark story in a
|
|
1068
1669
|
dark shell. It stays on the system's while comparing, since several themes share the stage. `appearance: "story"` makes
|
|
1069
1670
|
it the catalogue's default; a reader's own choice always wins.
|
|
1671
|
+
|
|
1070
1672
|
When the catalogue starts, a role that does not exist is named, and so is a hex colour that leaves a text or code under
|
|
1071
1673
|
4.5:1 on its ground, or the accent, which draws the keyboard's focus, under 3:1. Other colour notations are not checked.
|
|
1674
|
+
|
|
1072
1675
|
Fonts are yours to load: a web font goes in a style sheet of your own, `shell.css`, with `@font-face`. That sheet loads
|
|
1073
1676
|
after the shell's. It is also an escape hatch, since the shell's class names are not public API and can change in any
|
|
1074
1677
|
release. Layout is not themable, though a font does change how wide text runs.
|
|
@@ -1095,8 +1698,9 @@ it, and the Embed button's **Follow the story's height** adds the few lines that
|
|
|
1095
1698
|
iframe, since its modules are loaded as those of its own origin.
|
|
1096
1699
|
|
|
1097
1700
|
An embed sends nothing but the height and listens to nothing from the page around it, and it keeps working where a browser
|
|
1098
|
-
denies a third-party frame its storage. What it does take from outside is its address
|
|
1099
|
-
|
|
1701
|
+
denies a third-party frame its storage. What it does take from outside is its address, checked as the catalogue's own
|
|
1702
|
+
`args` are: a story receives only the props its panel can set, with a value of their type. A prop the story does declare
|
|
1703
|
+
still reaches it, so a story that renders a prop as raw HTML should not be published for anyone to frame.
|
|
1100
1704
|
And a height says something: a page that can reach your catalogue can tell whether a story exists.
|
|
1101
1705
|
|
|
1102
1706
|
Forgebook sets no framing headers, on the dev server or in a build, so what you host keeps the rules it has. To choose who may
|
|
@@ -1119,31 +1723,80 @@ missing-story messages, and the box a frame shows when a story fails.
|
|
|
1119
1723
|
|
|
1120
1724
|
## For agents
|
|
1121
1725
|
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1726
|
+
### Set a project up in one prompt
|
|
1727
|
+
|
|
1728
|
+
```sh
|
|
1729
|
+
npx forgebook init --agents --browser --draw --json
|
|
1730
|
+
```
|
|
1731
|
+
|
|
1732
|
+
Unattended, it writes the catalogue's files, then:
|
|
1733
|
+
|
|
1734
|
+
- **`.mcp.json`**, with Forgebook's server beside the ones it holds.
|
|
1735
|
+
- **`AGENTS.md`** (Forgebook's section, its other sections kept) **and the skill**, in `.agents/skills` and `.claude/skills`.
|
|
1736
|
+
- **A CI workflow**, `.github/workflows/forgebook-verify.yml`, running `forgebook verify` against a pull request's target.
|
|
1737
|
+
- **A hook that runs `verify`**, so an agent cannot finish on red. `--hooks` names them; by default those of the assistants
|
|
1738
|
+
the repository shows signs of, otherwise the commit's.
|
|
1739
|
+
|
|
1740
|
+
`--browser` installs `@forgebook/testing` and its browser, about 200 MB, and implies `--install`. `--draw` then writes a
|
|
1741
|
+
component's first stories and draws them in the browser, trying components until one draws and removing the stories of those
|
|
1742
|
+
that do not. If components fail for want of a provider, on React, the wrappers the app's entry mounts it in are copied into the
|
|
1743
|
+
preview as a decorator (`draw.liftedProviders`), and stay there if a story then draws. When none does, each attempt is in `draw.attempts` with a `kind` (`missing-import`, `provider`, `style-sheet`,
|
|
1744
|
+
`server-only`, `native-build`, `blank`…), what was said and what to do; the exit code is then 1.
|
|
1745
|
+
|
|
1746
|
+
`--agents` merges and never replaces what the project wrote (the skill's file is Forgebook's own, and is renewed): a JSON file it cannot parse, a link, a workflow or a commit hook already there stays as
|
|
1747
|
+
it is and is named in `nextSteps`. It keeps a catalogue already there and starts beside a Storybook, loading nothing of it.
|
|
1748
|
+
In a workspace's package, the files go at the repository's root, and find the package through its folder; a repository that
|
|
1749
|
+
does not track the package (a home folder kept in git, say) is not used, and they go in the package's folder.
|
|
1750
|
+
|
|
1751
|
+
| `--hooks` | Writes | When | On red |
|
|
1752
|
+
|---|---|---|---|
|
|
1753
|
+
| `claude` | `.claude/settings.json`, `Stop` | Claude Code is about to finish | Sent back (exit 2) |
|
|
1754
|
+
| `codex` | `.codex/hooks.json`, `Stop` | Codex is about to finish, once the hook is reviewed and trusted | Sent back (exit 2) |
|
|
1755
|
+
| `gemini` | `.gemini/settings.json`, `AfterAgent` | Gemini CLI's turn ends | Sent back (exit 2) |
|
|
1756
|
+
| `cursor` | `.cursor/hooks.json`, `stop` | Cursor's agent ends | Another turn starts with the reason: its `stop` hook cannot refuse |
|
|
1757
|
+
| `pre-commit` | `.husky/pre-commit`, else `.git/hooks/pre-commit` | A commit (`verify --quick`) | The commit is refused |
|
|
1758
|
+
|
|
1759
|
+
Those formats are the ones each assistant's documentation gives; no assistant was run against them here. An assistant not
|
|
1760
|
+
listed reads the instructions, and meets the commit's hook.
|
|
1761
|
+
|
|
1762
|
+
The hook is `forgebook hook <assistant>`. Each time an assistant stops, it loads the catalogue's configuration, so the
|
|
1763
|
+
project's code runs unattended there as it does for `verify`. A branch that changed no UI file since it left its default
|
|
1764
|
+
branch starts no server. A failure that comes back the same while the hook itself is what continued the turn lets the agent
|
|
1765
|
+
finish, told not to call the change done: an agent that cannot fix something must not spin on it. A hook that cannot run lets
|
|
1766
|
+
the agent finish, with a word on stderr, because no agent can repair it. The commit's hook refuses a commit the same way, on
|
|
1767
|
+
what the branch changed and not only what is staged; `git commit --no-verify` is the way past it, and a clone that has not
|
|
1768
|
+
installed yet commits as before.
|
|
1769
|
+
|
|
1770
|
+
`forgebook init` alone runs none of the project's code. `--draw` does, as starting a catalogue does: see [the app's
|
|
1771
|
+
build](#the-apps-build). An install runs what the project's package manager settings and the packages' scripts say.
|
|
1772
|
+
|
|
1773
|
+
### What agents read
|
|
1774
|
+
|
|
1775
|
+
A story's values can be set without the panel: `controls` in `forgebook story --json` and `get_story` says what each prop
|
|
1776
|
+
takes, and `forgebook story <id> --args '<json>'` or `story_address` with `args` checks the values as a link's are and
|
|
1777
|
+
answers the links that carry them ([the panel's controls](#the-panels-controls)).
|
|
1778
|
+
|
|
1779
|
+
`forgebook agents` gives a project's coding agents what they need without being told, as a section of `AGENTS.md` of under
|
|
1780
|
+
3 KB (renewed in place when it runs again; `--skill` writes the same as an Agent Skill any agent can load): what to run
|
|
1781
|
+
before saying a change is done, how to look things up, and how to write a story in this project's framework, read from its
|
|
1782
|
+
catalogue or its `package.json` (`--framework` names another). Agents work better with text they ask for than with a long
|
|
1783
|
+
file, so the rest waits for them: `forgebook guide` lists the topics (a framework, `stories`, `devices`, `switches`, `docs-pages`,
|
|
1784
|
+
`links`, `commands`, `setup`), `forgebook guide <topic>` prints one and `forgebook guide all` the whole guide.
|
|
1125
1785
|
|
|
1126
1786
|
A coding agent can use the catalogue while it works: find a story's id, read a story's args and its component's
|
|
1127
1787
|
props, and hand a person the link that opens it. Add the MCP server to the project's agent configuration, for
|
|
1128
1788
|
example `.mcp.json` for Claude Code:
|
|
1129
1789
|
|
|
1130
|
-
```
|
|
1131
|
-
|
|
1132
|
-
A library with design tokens (`tokens` in the configuration) can theme the shell from them in a few lines. Each role takes the
|
|
1133
|
-
token's value in both appearances, its `light` and `dark` themes first; colours written by hand win, and a token that
|
|
1134
|
-
is not there or is no colour is warned about and leaves the shell's own. The theme is read when the configuration is, so
|
|
1135
|
-
restart after editing the tokens.
|
|
1136
|
-
|
|
1137
|
-
```ts
|
|
1138
|
-
shell: { theme: { fromTokens: { accent: "color.brand.primary", surface: "color.background.card" } } }
|
|
1139
1790
|
```json
|
|
1140
1791
|
{ "mcpServers": { "forgebook": { "command": "npx", "args": ["forgebook", "mcp"] } } }
|
|
1141
1792
|
```
|
|
1142
1793
|
|
|
1143
|
-
It offers
|
|
1794
|
+
It offers 23 tools: `verify`, which says whether a change is done, `visual_diff`, `check_code`, `component_graph`, a map of the components and tokens to read instead of opening files, `check_page`, which checks a real page of the application, `list_stories`, `changed_stories`, `find_component`, which finds a component by what it is for, `get_story`, `story_address`, `new_stories`, which writes a component's first stories from its props and answers the text without writing a file, `check_story`, which runs the catalogue's browser checks on a story and answers its problems (the project needs `@forgebook/testing` and Playwright for it), `recent_errors`, what the frames open in a browser reported while the agent edited (numbered: `since` gives only the new ones), and `story_shot`, a story's pictures in the answer itself, so the agent looks at what it made, `list_pages` and `get_page`, which read the catalogue's docs pages as Markdown, `search_catalogue`, the search people use, `list_tokens`, `get_token` and `nearest_token`, the design tokens, `list_plugins`, what the catalogue's plugins add, `list_switches`, its switches and each value's view, and `get_guide`, the guide above, a topic at a time. `check`, `shot` and their tools take a view in the address's form (`theme=dark&width=360`) or as words (`"dark it 360"`); every story's `links.component` opens its component's page in that view, so `--view audience=designer` on `forgebook story` and `get_story` gives a link to send a designer. `get_story` also gives the components whose stories use the story's component (`usedIn`), so an agent finds a real use before writing one; `list_stories` gives it for every component when asked (`usedIn: true`, or `forgebook stories --used-in`), since it reads the package's import graph. Run from the catalogue's folder (or give
|
|
1144
1795
|
`--config`), it uses the running `forgebook dev` when there is one, and starts a dev server of its own otherwise,
|
|
1145
1796
|
kept while the agent's session lasts; that server keeps its pre-bundled dependencies in
|
|
1146
|
-
`node_modules/.vite/forgebook-commands`, apart from those of a `forgebook dev` running in the same folder
|
|
1797
|
+
`node_modules/.vite/forgebook-commands`, apart from those of a `forgebook dev` running in the same folder; a second
|
|
1798
|
+
command's server running at the same time (a `verify` while the MCP server serves) uses a folder of its own beside it,
|
|
1799
|
+
removed when it stops. The links name `http://localhost:5173` unless `--url` (or `FORGEBOOK_URL`) says
|
|
1147
1800
|
where `forgebook dev` runs.
|
|
1148
1801
|
|
|
1149
1802
|
`get_story` gives a story's design too (`design`, [above](#design-links)), so an agent can read the node with a Figma tool
|
|
@@ -1166,31 +1819,41 @@ page's words are replaceable as the shell's are (`strings`): `previewOpen`, `pre
|
|
|
1166
1819
|
|
|
1167
1820
|
A component's TSDoc can say what a person knows and an agent cannot guess: `@useWhen`, `@avoidWhen`, `@alternatives`,
|
|
1168
1821
|
`@deprecated` and `@replacedBy`. `forgebook story` and `get_story` return them as `docs.guidance`.
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1822
|
+
|
|
1823
|
+
The most common mistake of an agent working on a design system is a colour or a size written by hand. Agents can ask what
|
|
1824
|
+
to write instead, with nothing set up: Forgebook reads what the application already declares (see `check-code` below). To
|
|
1825
|
+
say it yourself, name your [DTCG](https://www.designtokens.org/tr/2025.10/) files in the configuration, the base first and
|
|
1826
|
+
each theme in a file of its own, and those are the system:
|
|
1827
|
+
|
|
1172
1828
|
```ts
|
|
1173
1829
|
tokens: ["tokens.json", { file: "dark.tokens.json", mode: "dark" }]
|
|
1174
1830
|
```
|
|
1831
|
+
|
|
1175
1832
|
A library gives its tokens to every catalogue that shows it: the same list on its source, by path from the module that
|
|
1176
1833
|
declares the source. It is read from that module's text, never by running it, so a broken story cannot hide a token: write
|
|
1177
1834
|
it in the object of `export const source`, with plain strings and paths inside the package. A source re-exported or
|
|
1178
1835
|
spread from another module gives no tokens.
|
|
1836
|
+
|
|
1179
1837
|
```ts
|
|
1180
1838
|
export const source: StorySource = { id: "acme-ui", tokens: ["tokens.json"] /* … */ };
|
|
1181
1839
|
```
|
|
1840
|
+
|
|
1182
1841
|
A catalogue reads each source's files in its `sources` order, then its own, so its aliases reach a library's palette. When
|
|
1183
1842
|
two authors give one token different values in the same theme the last stands, which makes the catalogue's own the final
|
|
1184
1843
|
word: it is what puts the libraries together. The answer says so in `clashes` (`kept`, `replaced`), the command warns,
|
|
1185
1844
|
and neither fails: a clash is a choice to review, not a token that cannot be read. A file over another may write only
|
|
1186
1845
|
`$value`: the token keeps its type, description and deprecation.
|
|
1846
|
+
|
|
1187
1847
|
```sh
|
|
1188
1848
|
npx forgebook tokens --category color --json
|
|
1189
1849
|
npx forgebook tokens --nearest "#1c1b1b" --json # the nearest tokens, and whether each is really close
|
|
1850
|
+
```
|
|
1851
|
+
|
|
1190
1852
|
`forgebook mcp` offers the same as `list_tokens`, `get_token` and `nearest_token`. References (`{a.b}`, `$ref`), `$extends`
|
|
1191
1853
|
and `$root` are followed, and a base alias follows what a theme overrides. Deprecated tokens are never suggested. Colours
|
|
1192
1854
|
in sRGB, Display P3, Oklab, Oklch and HSL are compared; other spaces are listed but counted as `uncompared`. A theme can
|
|
1193
|
-
reference the base; a `$ref` reaches only its own file.
|
|
1855
|
+
reference the base; a `$ref` reaches only its own file. A token whose aliases lead back to it, lead nowhere, or come to an
|
|
1856
|
+
empty value is named in `problems` and left out, since a browser would draw it with nothing.
|
|
1194
1857
|
|
|
1195
1858
|
An agent that cannot find a component writes a new one, often because the code and the designers use different words.
|
|
1196
1859
|
`forgebook build --agent-index` adds `forgebook-agent.json` to the built catalogue, and `forgebook mcp --from <address or
|
|
@@ -1201,7 +1864,9 @@ tokens, and anyone who reaches the site can read it, so it is written only when
|
|
|
1201
1864
|
`forgebook find` and `find_component` search names, the meta's `keywords`, titles and stories, with the usual synonyms
|
|
1202
1865
|
(`modal` finds a `Dialog`): write `keywords: ["radio tile group"]` on `OptionCards` and it is found.
|
|
1203
1866
|
`forgebook search` and `search_catalogue` read the rest, with the same ranking: a prop's name, a sentence of a docs page,
|
|
1204
|
-
each result with the words it missed and the address that opens the exact heading or prop.
|
|
1867
|
+
each result with the words it missed and the address that opens the exact heading or prop. Both answers carry
|
|
1868
|
+
`corrections`, each word read as another (`{ "typed": "buton", "read": "button" }`), empty when the query was read as
|
|
1869
|
+
typed.
|
|
1205
1870
|
|
|
1206
1871
|
`forgebook check <id> --semantic` also asks a model what axe cannot see: whether a heading describes its section, an
|
|
1207
1872
|
image's text says what it shows, a label says what its control does. It is off unless asked, and sends each story's
|
|
@@ -1210,10 +1875,155 @@ when it needs one, `FORGEBOOK_SEMANTIC_KEY`. No model comes with Forgebook and n
|
|
|
1210
1875
|
`judgements`, apart from the problems, as an opinion: it does not fail the check.
|
|
1211
1876
|
|
|
1212
1877
|
`forgebook check-code` and `check_code` read what a change adds, new files included, from where it left `--since`: a colour
|
|
1213
|
-
written by hand, with the token to use when one clearly fits; a size that equals a token
|
|
1878
|
+
written by hand, with the token to use when one clearly fits; a size that equals a token, in a sheet, a class's brackets
|
|
1879
|
+
or a React style object, where a bare number is pixels (`style={{ padding: 16 }}`, a `CSSProperties`, `styled.div({ … })`).
|
|
1880
|
+
A bare number on a line that does not say its object is a style is only `advice`, since an option has the same keys
|
|
1881
|
+
(`fitView({ padding: 16 })`). Then a check turned off with no
|
|
1214
1882
|
reason (`@ts-ignore`, `eslint-disable`, `biome-ignore`, `stylelint-disable`); a test deleted. Only styles and components
|
|
1215
1883
|
are read for values: stories, tests, token definitions and generated files are left alone. A fix is a token's name, not
|
|
1216
|
-
code to paste. It does not check props,
|
|
1884
|
+
code to paste. It does not check props, which the type checker does. It exits non-zero on a problem, for CI.
|
|
1885
|
+
|
|
1886
|
+
It also reads what a change reuses. An added import of a component whose own TSDoc says `@deprecated` or `@replacedBy` fails,
|
|
1887
|
+
naming what to use. A new exported component fails as `duplicate-component` when its code is a copy of another's under any name;
|
|
1888
|
+
or when it has the name of one that exists, past words like `Primary` or `Custom`, and two signs of being it (a telling root
|
|
1889
|
+
such as `button`, shared props, nearly the same classes); or when `Button` exists and a new `CustomButton` renders a plain
|
|
1890
|
+
`<button>`, though not a `SubmitButton`, which says what it is for. New means new since the commit compared with: an edit or a
|
|
1891
|
+
moved file is not. It compares only what the file could use, in its package and those its package depends on. It passes if it
|
|
1892
|
+
renders the other, which is reuse, or if a comment says why, above it: `// forgebook-ignore duplicate-component: why`, listed
|
|
1893
|
+
in `excused`. One sign, and what `@useWhen` and `@avoidWhen` say of a component just imported, are `advice`, which fails nothing.
|
|
1894
|
+
Imports are followed relative, through `@/`, `~/` and `#/` under the importing file's own folders, and by a workspace package's
|
|
1895
|
+
name; not through a third party.
|
|
1896
|
+
|
|
1897
|
+
**It needs no tokens file.** With none named, it compares with what the application already declares, read as text and never
|
|
1898
|
+
run: custom properties at the root and in theme selectors (`.dark`, `[data-theme]`, a colour-scheme query), and a selector
|
|
1899
|
+
holding a dozen or more of them, as a library's root does; Tailwind 4's `@theme` and default theme; a Tailwind configuration
|
|
1900
|
+
and its presets, as far as they are written plainly; the sheet and the configuration `components.json` names. `tokenSources`
|
|
1901
|
+
says which files were read and `tokenNotes` what needs running to know. A token a browser would give no value is a problem,
|
|
1902
|
+
as a broken tokens file is: one whose references lead back to itself, or one that reads a name with no fallback that
|
|
1903
|
+
nothing declares: no sheet read, no `@property`, no package sheet it imports, and no file of the repository that sets it
|
|
1904
|
+
(in quotes, as `next/font`'s `variable: "--font-sans"`, or as a declaration in a template or a `.vue` style block).
|
|
1905
|
+
Tailwind's `@theme inline { --font-sans: var(--font-sans); }` reads the name set elsewhere, which is no cycle. Where that
|
|
1906
|
+
cannot be known (a package sheet not read, names a Sass loop builds, a cycle only inside a theme, which depends on the
|
|
1907
|
+
element the theme is set on), the token is a note instead, which fails nothing. A name only code builds (`` `--${key}` ``)
|
|
1908
|
+
is not seen: give such a reference a fallback. Fewer than five colours is not a palette, so colours
|
|
1909
|
+
are then left to `skipped`. A value it cannot judge takes a comment on its line or the one above, with the reason:
|
|
1910
|
+
`/* forgebook-ignore raw-color: a brand's own colour */`; the answer lists each one in `excused`, with its reason. Only files
|
|
1911
|
+
in the catalogue's folder are compared (`notCompared` names the others), since another package has its own system. Nothing
|
|
1912
|
+
outside the repository is read, whatever a file names, and a catalogue's `build --agent-index` carries these tokens. `tokens:
|
|
1913
|
+
[]` in the configuration says the application is not to be read: with no source giving tokens, values are then not checked,
|
|
1914
|
+
`colours and sizes not checked: no tokens file`.
|
|
1915
|
+
|
|
1916
|
+
What it leaves alone on purpose, since these are where false alarms came from on real projects: a drawing's colours (`<path
|
|
1917
|
+
fill>`, gradient stops, a canvas), masks, fully transparent ends of gradients, black or white in a shadow, a `var()`'s fallback,
|
|
1918
|
+
what the page's head reads (`theme-color`), emails and generated social images. A size is compared with tokens of its own
|
|
1919
|
+
role (spacing, radius, font size) that are steps of a scale, so `--sidebar-padding: 16px` does not make every `16px` a
|
|
1920
|
+
mistake; Tailwind's own scale counts only where a class's brackets hold the whole value (`p-[16px]`, not `h-[calc(100%-16px)]`).
|
|
1921
|
+
|
|
1922
|
+
A design system can say how to choose among fixed patterns and how its parts fit, in a few tags of a component's TSDoc, with
|
|
1923
|
+
nothing new to learn:
|
|
1924
|
+
|
|
1925
|
+
```ts
|
|
1926
|
+
/**
|
|
1927
|
+
* Pick one of a few options, all in view.
|
|
1928
|
+
*
|
|
1929
|
+
* @intent choose-one: up to five options, all visible
|
|
1930
|
+
* @parent Form, Fieldset
|
|
1931
|
+
* @children Radio
|
|
1932
|
+
* @insteadOf select
|
|
1933
|
+
*/
|
|
1934
|
+
export const RadioGroup = …
|
|
1935
|
+
```
|
|
1936
|
+
|
|
1937
|
+
Components sharing an `@intent` are the choices for it, and agents read them with `forgebook graph --intent choose-one` or
|
|
1938
|
+
`component_graph`, or as `advice` when they import one. `check-code` enforces what can be checked, in `.tsx` and `.jsx` only,
|
|
1939
|
+
as `composition`: an element under a component `@parent` does not allow; a component `@children` or `@siblings` does not allow;
|
|
1940
|
+
a plain `<select>` where `@insteadOf` names one. An element at the root of what a component returns, or held in a prop, may be
|
|
1941
|
+
rendered anywhere, so its parent is not judged; a component above that itself renders an allowed parent passes; and which pattern a screen should have used is not something a check can tell. A file beside a
|
|
1942
|
+
component that cannot carry a comment says the same, named after the file or the component: `radio-group.rules.json` or
|
|
1943
|
+
`RadioGroup.rules.json`, `{ "RadioGroup": { "intent": ["choose-one: up to five options"], "parent": ["Form"] } }`. A meant exception takes `// forgebook-ignore composition:
|
|
1944
|
+
why`, listed in `excused`.
|
|
1945
|
+
|
|
1946
|
+
`forgebook graph` is the map itself, generated from the source so it cannot drift: components, what each is for, what renders
|
|
1947
|
+
what, token counts; `--component Button` adds what renders it and the tokens its file uses. It needs no catalogue.
|
|
1948
|
+
|
|
1949
|
+
`forgebook audit` reports what a project actually uses, read from its files, with no catalogue, no configuration and no code
|
|
1950
|
+
run: `37 greys, 5 used 90 % of the time`. It is for a project with no design system as much as one with: colours in any
|
|
1951
|
+
notation counted as one, how many go through tokens and how many are written by hand, sizes by what they are for, and
|
|
1952
|
+
components that look like one, by name and by what they render, take and carry, never by name alone. It also says where
|
|
1953
|
+
a catalogue rots: exported components no story file imports (`withoutStories`), and components with stories that nothing
|
|
1954
|
+
but stories and tests imports or draws (`storiesUnused`), each with its story files. Pages, layouts and routes are in
|
|
1955
|
+
neither list. A package that goes to a registry (named and not private) never has its components called unused, since
|
|
1956
|
+
their users are elsewhere; a workspace's private package is judged by what the repository does with it, so audit the
|
|
1957
|
+
repository's root to see its users. An import it cannot follow (a barrel, an alias, a package's name) counts for every
|
|
1958
|
+
component of that name, so a listed component is one nothing in the project could be. It exits 0: it is a report, and `check-code` is what fails. With `--json`, `actions` lists the next steps, the largest first, each with the
|
|
1959
|
+
command that lists what to edit:
|
|
1960
|
+
|
|
1961
|
+
```sh
|
|
1962
|
+
npx forgebook audit --json
|
|
1963
|
+
npx forgebook audit --value "#64748b" --json # every line that writes it by hand
|
|
1964
|
+
```
|
|
1965
|
+
|
|
1966
|
+
It does not read a tokens file a catalogue names, only what the application declares, and a `.js` file's JSX is not read.
|
|
1967
|
+
A component a framework registers by its folder (Nuxt's `<BaseButton>` for `components/base/Button.vue`) is matched only by
|
|
1968
|
+
its own name, so it can be listed as unused.
|
|
1969
|
+
|
|
1970
|
+
`forgebook verify` and `verify` are what an agent runs before saying a change is done, and what a pull request's workflow
|
|
1971
|
+
runs. A change cannot pass on nothing:
|
|
1972
|
+
|
|
1973
|
+
```sh
|
|
1974
|
+
$ npx forgebook verify --since origin/develop
|
|
1975
|
+
src/Badge.tsx not verified: no story reaches it. Fix: npx forgebook new src/Badge.tsx
|
|
1976
|
+
2 changed UI files, 1 with stories: not verified; colours and sizes not checked: no tokens file
|
|
1977
|
+
```
|
|
1978
|
+
|
|
1979
|
+
- **Every changed UI file is covered or fails.** A UI file is a component or a style sheet by its extension (`.tsx`, `.jsx`,
|
|
1980
|
+
`.vue`, `.svelte`, `.astro`, `.component.ts`, `.component.html`, `.css`, `.scss`, `.sass`, `.less`), a story file, or a
|
|
1981
|
+
script whose text defines or draws a component (`customElements.define`, `LitElement`, `@Component`, `defineComponent`,
|
|
1982
|
+
JSX in a `.js` file). Tests, type declarations and generated files are not, and neither is any other script: a helper
|
|
1983
|
+
or a hook does not fail, and the stories it reaches still run. What `check-code` finds in a file no one sees (a turned-off
|
|
1984
|
+
check, a deleted test) is a note in the answer; `check-code` itself still fails on it.
|
|
1985
|
+
- **A file used through another component counts, and says so.** Stories reach a file through what they render and what
|
|
1986
|
+
those files import, by the file that declares each imported name, so being exported from a barrel covers nothing. A
|
|
1987
|
+
sheet counts through whatever imports it, `@use` and `@import` followed; a preview and the sheets it names are shown by
|
|
1988
|
+
every story. Only stories that ran count: a project with no browser gets `not-verified`, and the reason.
|
|
1989
|
+
- **What was not checked is never reported as passed.** `skipped` is in the verdict (`partly-verified`) and in the line:
|
|
1990
|
+
colours and sizes without a tokens file or with one that cannot be read, accessibility with `a11y: false`, a UI file only
|
|
1991
|
+
another catalogue of the repository can answer for. None of these fails. A UI file nothing reaches in a package with no
|
|
1992
|
+
catalogue of its own does: it is this catalogue's to show. So does a package whose types point at its build, since
|
|
1993
|
+
imports stop there.
|
|
1994
|
+
- **Only what the change brought fails.** The stories that found something run again on the commit the change starts
|
|
1995
|
+
from, checked out in a worktree beside the repository (`.<repository>-forgebook-base-<commit>-<process>`) and removed
|
|
1996
|
+
after, the working tree untouched; the base's configuration and stories run there, as yours do. A problem
|
|
1997
|
+
they had there too is set apart in `existing` and said in the line ("2 problems the stories had before the change not
|
|
1998
|
+
counted"), so an application whose stories already break a rule can still be verified. `--all-problems` fails on
|
|
1999
|
+
every problem. When the base cannot run, every problem counts and `skipped` says why.
|
|
2000
|
+
- **Stories written for other props fail.** A story file `forgebook new` wrote says which props it was written for
|
|
2001
|
+
(`// forgebook-props: 1a2b3c4d`). When a change reaches it and the component's props no longer match, `verify` lists the
|
|
2002
|
+
file in `outdated` and fails, since a framework ignores a prop that is gone and never shows one that is new: check the
|
|
2003
|
+
args, then set the line to the signature it names, or write the file again with `forgebook new <component file> --force`, which replaces any edit.
|
|
2004
|
+
- **It compares from where the branch left.** `--since <ref>` starts from the merge base, so what the base gained since is
|
|
2005
|
+
not read as your change; with no `--since`, from the default branch (`origin/HEAD`, then `main` or `master`), so what you
|
|
2006
|
+
committed counts. A branch cut from another, as `develop`, gives it with `--since`. With no `--since`, an empty change
|
|
2007
|
+
is `not-verified`: the work may be committed where this cannot see, as in a shallow clone. CI gives `--since` and the
|
|
2008
|
+
history: `fetch-depth: 0`.
|
|
2009
|
+
- **A published component an agent cannot see fails.** A component a package's entries export, when the change edits it or
|
|
2010
|
+
starts exporting it, needs a story about it (its meta names it, or a search finds it by name), one story not tagged
|
|
2011
|
+
`!dev`, props an agent can read, and TSDoc on it or a `docs.description.component`. Each finding names its fix. A part
|
|
2012
|
+
named after its component (`DialogTrigger`) is seen on that page, and a provider owes no story. An application publishes
|
|
2013
|
+
nothing, so this asks nothing of it. `forgebook visibility` lists every such component in the repository, for a first look:
|
|
2014
|
+
|
|
2015
|
+
```sh
|
|
2016
|
+
$ npx forgebook visibility
|
|
2017
|
+
src/Avatar.tsx agents cannot see it: no story is about Avatar, so a search for it finds nothing. Fix: npx forgebook new src/Avatar.tsx
|
|
2018
|
+
12 published component files, 1 agents cannot see.
|
|
2019
|
+
```
|
|
2020
|
+
- **Every variant by default**, each story in light, dark, both at more contrast, the structure only, and narrow, on one
|
|
2021
|
+
browser, four pages at once. `--quick` runs the default view, and the verdict says so.
|
|
2022
|
+
|
|
2023
|
+
The exit is non-zero on `failed` and `not-verified`, zero on `verified`, `partly-verified` and `nothing-to-verify`. The
|
|
2024
|
+
JSON answer is schema 2: `verdict`, `summary`, `skipped`, `since`, `base`, `variants`, `files`, `uiFiles` (each with its
|
|
2025
|
+
`coverage`, and its `reason` and `fix` when not covered), `storyCount`, `stories` (the first twenty, with their links),
|
|
2026
|
+
`ran`, `failures` (only what found something), `notVerified` (stories still loading when the frame gave up), `code` (`checked`, `problems`, `notes`) and `problems`.
|
|
1217
2027
|
|
|
1218
2028
|
A library tells agents what it offers with `forgebook docs`, run in its folder: `CATALOG.md`, one dense line per
|
|
1219
2029
|
component, hook, helper and style module (what it is, how to import it, its props, the id of a story that shows it),
|
|
@@ -1225,9 +2035,42 @@ package's `package.json`:
|
|
|
1225
2035
|
{ "forgebook": { "catalog": { "file": "CATALOG.md", "sections": ["components", "pages", "modules"], "exclude": ["./internal/*"] } } }
|
|
1226
2036
|
```
|
|
1227
2037
|
|
|
1228
|
-
`forgebook docs --check` fails in CI when either file is stale. In CI, `forgebook stories --json` fails on a broken story file (a title taken twice, a title that cannot be placed) or a
|
|
2038
|
+
`forgebook docs --check` fails in CI when either file is stale. In CI, `forgebook stories --json` fails on a broken story file (a title taken twice, a title that cannot be placed, a file that does not parse) or a
|
|
1229
2039
|
docs page that names what is not there, and its JSON lists every story for a script.
|
|
1230
2040
|
|
|
2041
|
+
## Upgrading
|
|
2042
|
+
|
|
2043
|
+
While Forgebook is 0.x, a minor version (0.3 to 0.4) may break, and a patch never does. Every breaking change is in the
|
|
2044
|
+
package's changelog with what to do, and where the rewrite is mechanical, `forgebook migrate` makes it:
|
|
2045
|
+
|
|
2046
|
+
```sh
|
|
2047
|
+
npx forgebook migrate --from 0.3.3 # lists what changes since 0.3.3, writes nothing
|
|
2048
|
+
npx forgebook migrate --from 0.3.3 --write # makes the changes
|
|
2049
|
+
npx forgebook migrate src/preset.ts # a library's preset module too, beside the configuration
|
|
2050
|
+
```
|
|
2051
|
+
|
|
2052
|
+
It reads the configuration and the files given as text, runs none of their code, and keeps their formatting: only what a
|
|
2053
|
+
codemod rewrites moves, and each file is written whole or not at all. A file given must be inside the folder it runs in
|
|
2054
|
+
and outside `node_modules`. Run twice, it finds nothing the second time. It exits 1 while it lists changes it has not
|
|
2055
|
+
written, and 0 otherwise: `advice` (what a codemod could not see into, such as a configuration built by a function) and
|
|
2056
|
+
`notes` (the breaking changes no codemod can make) are to read, and leave the exit code at 0. `--json` answers `changes`,
|
|
2057
|
+
`advice`, `skipped` and `notes`. With no `--from` it runs only the rewrites of what no longer works; a codemod that keeps
|
|
2058
|
+
an old behaviour (`play: "on"`) runs when `--from` says the project comes from before it, or by name with `--only`.
|
|
2059
|
+
`--from` names `forgebook`'s version; a note of another package says that package's version and is always listed.
|
|
2060
|
+
|
|
2061
|
+
| From forgebook 0.3 to 0.4 | `forgebook migrate` |
|
|
2062
|
+
|---|---|
|
|
2063
|
+
| The strings `propGroups.choice`, `.text`, `.structured` and `.toggle` are gone: the Props tab lists props in their source's order | removes them from `strings` (`prop-group-strings`) |
|
|
2064
|
+
| A built catalogue no longer runs a story's play when it opens | writes `play: "on"` to keep it (`play-on-in-builds`); leave it out for the new default |
|
|
2065
|
+
| A configuration key nothing reads, a toolbar naming no control and a story pattern matching no file stop the commands, which name them | in `notes`; fix what they name |
|
|
2066
|
+
| The catalogue draws stories with the app's own build: tsconfig paths, the `vite.config` beside it (its code runs) and PostCSS | in `notes`; `appBuild: false`, or one part off, for a catalogue that set them up by hand |
|
|
2067
|
+
| `verify` fails on a changed UI file with no story that ran, its JSON is schema 2, and it fails on a published component agents cannot see | in `notes` |
|
|
2068
|
+
| `check-code` reads the application's design system with no tokens file, compares bare sizes in React style objects, and fails on duplicate and deprecated components | in `notes`; `tokens: []` says there is no system |
|
|
2069
|
+
| `@forgebook/story-format` 0.3: a story file's code entries are `StoryCode \| StoryArgsCode` | in `notes`; narrow with `"text" in entry` |
|
|
2070
|
+
|
|
2071
|
+
What counts as breaking, how long a deprecation lasts and what must be true before 1.0:
|
|
2072
|
+
[the version policy](https://github.com/ensiria/forgebook/blob/develop/CONTRIBUTING.md#version-policy).
|
|
2073
|
+
|
|
1231
2074
|
## Packages
|
|
1232
2075
|
|
|
1233
2076
|
| Package | What it is |
|