@zeix/le-truc 0.15.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/.ai-context.md +234 -0
- package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +111 -0
- package/.editorconfig +12 -0
- package/.github/copilot-instructions.md +62 -0
- package/.github/workflows/codeql.yml +108 -0
- package/.github/workflows/static.yml +43 -0
- package/.prettierrc +17 -0
- package/CLAUDE.md +215 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +160 -0
- package/LICENSE +21 -0
- package/README.md +474 -0
- package/biome.json +295 -0
- package/bun.lock +239 -0
- package/docs/about.html +105 -0
- package/docs/assets/main.css +1 -0
- package/docs/assets/main.js +10 -0
- package/docs/assets/main.js.map +66 -0
- package/docs/components.html +293 -0
- package/docs/data-flow.html +308 -0
- package/docs/examples/basic-button.html +367 -0
- package/docs/examples/basic-counter.html +188 -0
- package/docs/examples/basic-hello.html +138 -0
- package/docs/examples/basic-number.html +271 -0
- package/docs/examples/basic-pluralize.html +214 -0
- package/docs/examples/card-callout.html +152 -0
- package/docs/examples/card-mediaqueries.html +138 -0
- package/docs/examples/context-media.html +198 -0
- package/docs/examples/empty.html +37 -0
- package/docs/examples/form-checkbox.html +233 -0
- package/docs/examples/form-combobox.html +420 -0
- package/docs/examples/form-listbox.html +434 -0
- package/docs/examples/form-radiogroup.html +296 -0
- package/docs/examples/form-spinbutton.html +402 -0
- package/docs/examples/form-textbox.html +361 -0
- package/docs/examples/layout.html +67 -0
- package/docs/examples/module-carousel.html +552 -0
- package/docs/examples/module-catalog.html +241 -0
- package/docs/examples/module-codeblock.html +270 -0
- package/docs/examples/module-dialog.html +343 -0
- package/docs/examples/module-lazyload.html +289 -0
- package/docs/examples/module-list.html +197 -0
- package/docs/examples/module-pagination.html +283 -0
- package/docs/examples/module-scrollarea.html +447 -0
- package/docs/examples/module-tabgroup.html +526 -0
- package/docs/examples/module-todo.html +367 -0
- package/docs/examples/module-with-type.html +63 -0
- package/docs/examples/nested-components.html +88 -0
- package/docs/examples/recursive.html +56 -0
- package/docs/examples/simple-text.html +39 -0
- package/docs/examples/snippet.html +93 -0
- package/docs/examples/with-styles.html +75 -0
- package/docs/getting-started.html +143 -0
- package/docs/index.html +112 -0
- package/docs/sitemap.xml +28 -0
- package/docs/styling.html +160 -0
- package/docs/sw.js +112 -0
- package/docs-src/api/README.md +478 -0
- package/docs-src/api/_media/LICENSE +21 -0
- package/docs-src/api/classes/CircularDependencyError.md +299 -0
- package/docs-src/api/classes/CircularMutationError.md +301 -0
- package/docs-src/api/classes/ContextRequestEvent.md +590 -0
- package/docs-src/api/classes/DependencyTimeoutError.md +301 -0
- package/docs-src/api/classes/InvalidCallbackError.md +303 -0
- package/docs-src/api/classes/InvalidComponentNameError.md +295 -0
- package/docs-src/api/classes/InvalidCustomElementError.md +301 -0
- package/docs-src/api/classes/InvalidEffectsError.md +301 -0
- package/docs-src/api/classes/InvalidPropertyNameError.md +307 -0
- package/docs-src/api/classes/InvalidReactivesError.md +307 -0
- package/docs-src/api/classes/InvalidSignalValueError.md +303 -0
- package/docs-src/api/classes/MissingElementError.md +307 -0
- package/docs-src/api/classes/NullishSignalValueError.md +299 -0
- package/docs-src/api/classes/StoreKeyExistsError.md +303 -0
- package/docs-src/api/classes/StoreKeyRangeError.md +299 -0
- package/docs-src/api/classes/StoreKeyReadonlyError.md +303 -0
- package/docs-src/api/functions/asBoolean.md +21 -0
- package/docs-src/api/functions/asEnum.md +31 -0
- package/docs-src/api/functions/asInteger.md +39 -0
- package/docs-src/api/functions/asJSON.md +49 -0
- package/docs-src/api/functions/asNumber.md +37 -0
- package/docs-src/api/functions/asString.md +37 -0
- package/docs-src/api/functions/createCollection.md +83 -0
- package/docs-src/api/functions/createSensor.md +71 -0
- package/docs-src/api/functions/dangerouslySetInnerHTML.md +48 -0
- package/docs-src/api/functions/defineComponent.md +65 -0
- package/docs-src/api/functions/isCollection.md +37 -0
- package/docs-src/api/functions/isParser.md +41 -0
- package/docs-src/api/functions/match.md +47 -0
- package/docs-src/api/functions/on.md +58 -0
- package/docs-src/api/functions/pass.md +53 -0
- package/docs-src/api/functions/provideContexts.md +47 -0
- package/docs-src/api/functions/read.md +47 -0
- package/docs-src/api/functions/requestContext.md +51 -0
- package/docs-src/api/functions/resolve.md +40 -0
- package/docs-src/api/functions/runEffects.md +51 -0
- package/docs-src/api/functions/runElementEffects.md +57 -0
- package/docs-src/api/functions/schedule.md +33 -0
- package/docs-src/api/functions/setAttribute.md +48 -0
- package/docs-src/api/functions/setProperty.md +52 -0
- package/docs-src/api/functions/setStyle.md +48 -0
- package/docs-src/api/functions/setText.md +42 -0
- package/docs-src/api/functions/show.md +42 -0
- package/docs-src/api/functions/toSignal.md +37 -0
- package/docs-src/api/functions/toggleAttribute.md +48 -0
- package/docs-src/api/functions/toggleClass.md +48 -0
- package/docs-src/api/functions/updateElement.md +53 -0
- package/docs-src/api/globals.md +131 -0
- package/docs-src/api/type-aliases/Cleanup.md +27 -0
- package/docs-src/api/type-aliases/Collection.md +91 -0
- package/docs-src/api/type-aliases/CollectionListener.md +27 -0
- package/docs-src/api/type-aliases/Component.md +17 -0
- package/docs-src/api/type-aliases/ComponentProp.md +11 -0
- package/docs-src/api/type-aliases/ComponentProps.md +11 -0
- package/docs-src/api/type-aliases/ComponentSetup.md +31 -0
- package/docs-src/api/type-aliases/ComponentUI.md +27 -0
- package/docs-src/api/type-aliases/Computed.md +49 -0
- package/docs-src/api/type-aliases/ComputedCallback.md +29 -0
- package/docs-src/api/type-aliases/Context.md +33 -0
- package/docs-src/api/type-aliases/ContextType.md +19 -0
- package/docs-src/api/type-aliases/DangerouslySetInnerHTMLOptions.md +27 -0
- package/docs-src/api/type-aliases/DiffResult.md +61 -0
- package/docs-src/api/type-aliases/Effect.md +35 -0
- package/docs-src/api/type-aliases/EffectCallback.md +23 -0
- package/docs-src/api/type-aliases/Effects.md +21 -0
- package/docs-src/api/type-aliases/ElementEffects.md +21 -0
- package/docs-src/api/type-aliases/ElementFromKey.md +21 -0
- package/docs-src/api/type-aliases/ElementQueries.md +27 -0
- package/docs-src/api/type-aliases/ElementUpdater.md +131 -0
- package/docs-src/api/type-aliases/EventHandler.md +31 -0
- package/docs-src/api/type-aliases/EventType.md +17 -0
- package/docs-src/api/type-aliases/Fallback.md +21 -0
- package/docs-src/api/type-aliases/Initializers.md +21 -0
- package/docs-src/api/type-aliases/LooseReader.md +31 -0
- package/docs-src/api/type-aliases/MatchHandlers.md +77 -0
- package/docs-src/api/type-aliases/MaybeCleanup.md +23 -0
- package/docs-src/api/type-aliases/MaybeSignal.md +17 -0
- package/docs-src/api/type-aliases/Parser.md +39 -0
- package/docs-src/api/type-aliases/ParserOrFallback.md +21 -0
- package/docs-src/api/type-aliases/PassedProp.md +25 -0
- package/docs-src/api/type-aliases/PassedProps.md +21 -0
- package/docs-src/api/type-aliases/Reactive.md +25 -0
- package/docs-src/api/type-aliases/Reader.md +31 -0
- package/docs-src/api/type-aliases/ReservedWords.md +11 -0
- package/docs-src/api/type-aliases/ResolveResult.md +29 -0
- package/docs-src/api/type-aliases/SensorEvents.md +25 -0
- package/docs-src/api/type-aliases/Signal.md +41 -0
- package/docs-src/api/type-aliases/State.md +85 -0
- package/docs-src/api/type-aliases/Store.md +29 -0
- package/docs-src/api/type-aliases/UI.md +11 -0
- package/docs-src/api/type-aliases/UnknownContext.md +13 -0
- package/docs-src/api/variables/CONTEXT_REQUEST.md +11 -0
- package/docs-src/api/variables/UNSET.md +23 -0
- package/docs-src/api/variables/batch.md +25 -0
- package/docs-src/api/variables/createComputed.md +41 -0
- package/docs-src/api/variables/createEffect.md +35 -0
- package/docs-src/api/variables/createState.md +37 -0
- package/docs-src/api/variables/createStore.md +42 -0
- package/docs-src/api/variables/diff.md +43 -0
- package/docs-src/api/variables/isAbortError.md +33 -0
- package/docs-src/api/variables/isAsyncFunction.md +39 -0
- package/docs-src/api/variables/isComputed.md +37 -0
- package/docs-src/api/variables/isEqual.md +49 -0
- package/docs-src/api/variables/isFunction.md +39 -0
- package/docs-src/api/variables/isMutableSignal.md +37 -0
- package/docs-src/api/variables/isNumber.md +33 -0
- package/docs-src/api/variables/isRecord.md +39 -0
- package/docs-src/api/variables/isRecordOrArray.md +39 -0
- package/docs-src/api/variables/isSignal.md +37 -0
- package/docs-src/api/variables/isState.md +37 -0
- package/docs-src/api/variables/isStore.md +37 -0
- package/docs-src/api/variables/isString.md +33 -0
- package/docs-src/api/variables/isSymbol.md +33 -0
- package/docs-src/api/variables/toError.md +33 -0
- package/docs-src/api/variables/valueString.md +33 -0
- package/docs-src/includes/menu.html +44 -0
- package/docs-src/pages/about.md +89 -0
- package/docs-src/pages/components.md +437 -0
- package/docs-src/pages/data-flow.md +449 -0
- package/docs-src/pages/getting-started.md +170 -0
- package/docs-src/pages/index.md +98 -0
- package/docs-src/pages/styling.md +165 -0
- package/eslint.config.js +64 -0
- package/examples/_common/clear.ts +49 -0
- package/examples/_common/fetch.ts +160 -0
- package/examples/_common/focus.ts +45 -0
- package/examples/_common/highlight.ts +5 -0
- package/examples/_global.css +463 -0
- package/examples/basic-button/basic-button.css +176 -0
- package/examples/basic-button/basic-button.html +46 -0
- package/examples/basic-button/basic-button.spec.ts +160 -0
- package/examples/basic-button/basic-button.ts +45 -0
- package/examples/basic-button/copyToClipboard.ts +37 -0
- package/examples/basic-counter/basic-counter.css +21 -0
- package/examples/basic-counter/basic-counter.html +24 -0
- package/examples/basic-counter/basic-counter.spec.ts +85 -0
- package/examples/basic-counter/basic-counter.ts +43 -0
- package/examples/basic-hello/basic-hello.html +34 -0
- package/examples/basic-hello/basic-hello.spec.ts +110 -0
- package/examples/basic-hello/basic-hello.ts +36 -0
- package/examples/basic-number/basic-number.html +79 -0
- package/examples/basic-number/basic-number.spec.ts +175 -0
- package/examples/basic-number/basic-number.ts +124 -0
- package/examples/basic-pluralize/basic-pluralize.html +64 -0
- package/examples/basic-pluralize/basic-pluralize.spec.ts +258 -0
- package/examples/basic-pluralize/basic-pluralize.ts +82 -0
- package/examples/card-callout/card-callout.css +79 -0
- package/examples/card-callout/card-callout.html +5 -0
- package/examples/card-mediaqueries/card-mediaqueries.html +29 -0
- package/examples/card-mediaqueries/card-mediaqueries.spec.ts +300 -0
- package/examples/card-mediaqueries/card-mediaqueries.ts +41 -0
- package/examples/context-media/context-media.html +3 -0
- package/examples/context-media/context-media.ts +127 -0
- package/examples/form-checkbox/form-checkbox.css +70 -0
- package/examples/form-checkbox/form-checkbox.html +13 -0
- package/examples/form-checkbox/form-checkbox.spec.ts +357 -0
- package/examples/form-checkbox/form-checkbox.ts +50 -0
- package/examples/form-checkbox/vanilla-checkbox.ts +101 -0
- package/examples/form-combobox/form-combobox.css +118 -0
- package/examples/form-combobox/form-combobox.html +74 -0
- package/examples/form-combobox/form-combobox.spec.ts +977 -0
- package/examples/form-combobox/form-combobox.ts +128 -0
- package/examples/form-listbox/form-listbox.css +71 -0
- package/examples/form-listbox/form-listbox.html +67 -0
- package/examples/form-listbox/form-listbox.spec.ts +1050 -0
- package/examples/form-listbox/form-listbox.ts +196 -0
- package/examples/form-listbox/mocks/timezones.json +495 -0
- package/examples/form-radiogroup/form-radiogroup.css +87 -0
- package/examples/form-radiogroup/form-radiogroup.html +51 -0
- package/examples/form-radiogroup/form-radiogroup.spec.ts +515 -0
- package/examples/form-radiogroup/form-radiogroup.ts +58 -0
- package/examples/form-spinbutton/form-spinbutton.css +95 -0
- package/examples/form-spinbutton/form-spinbutton.html +96 -0
- package/examples/form-spinbutton/form-spinbutton.spec.ts +688 -0
- package/examples/form-spinbutton/form-spinbutton.ts +111 -0
- package/examples/form-textbox/form-textbox.css +104 -0
- package/examples/form-textbox/form-textbox.html +53 -0
- package/examples/form-textbox/form-textbox.spec.ts +542 -0
- package/examples/form-textbox/form-textbox.ts +104 -0
- package/examples/main.css +22 -0
- package/examples/main.ts +23 -0
- package/examples/module-carousel/module-carousel.css +113 -0
- package/examples/module-carousel/module-carousel.html +208 -0
- package/examples/module-carousel/module-carousel.spec.ts +523 -0
- package/examples/module-carousel/module-carousel.ts +131 -0
- package/examples/module-catalog/module-catalog.css +22 -0
- package/examples/module-catalog/module-catalog.html +82 -0
- package/examples/module-catalog/module-catalog.spec.ts +396 -0
- package/examples/module-catalog/module-catalog.ts +37 -0
- package/examples/module-codeblock/module-codeblock.css +95 -0
- package/examples/module-codeblock/module-codeblock.html +28 -0
- package/examples/module-codeblock/module-codeblock.ts +47 -0
- package/examples/module-demo/module-demo.css +13 -0
- package/examples/module-dialog/module-dialog.css +96 -0
- package/examples/module-dialog/module-dialog.html +66 -0
- package/examples/module-dialog/module-dialog.spec.ts +557 -0
- package/examples/module-dialog/module-dialog.ts +81 -0
- package/examples/module-lazyload/mocks/empty.html +1 -0
- package/examples/module-lazyload/mocks/module-with-type.html +27 -0
- package/examples/module-lazyload/mocks/nested-components.html +52 -0
- package/examples/module-lazyload/mocks/recursive.html +20 -0
- package/examples/module-lazyload/mocks/simple-text.html +3 -0
- package/examples/module-lazyload/mocks/snippet.html +57 -0
- package/examples/module-lazyload/mocks/with-styles.html +39 -0
- package/examples/module-lazyload/module-lazyload.html +132 -0
- package/examples/module-lazyload/module-lazyload.spec.ts +734 -0
- package/examples/module-lazyload/module-lazyload.ts +89 -0
- package/examples/module-list/module-list.html +30 -0
- package/examples/module-list/module-list.spec.ts +592 -0
- package/examples/module-list/module-list.ts +99 -0
- package/examples/module-pagination/module-pagination.css +79 -0
- package/examples/module-pagination/module-pagination.html +16 -0
- package/examples/module-pagination/module-pagination.spec.ts +701 -0
- package/examples/module-pagination/module-pagination.ts +88 -0
- package/examples/module-scrollarea/module-scrollarea.css +77 -0
- package/examples/module-scrollarea/module-scrollarea.html +189 -0
- package/examples/module-scrollarea/module-scrollarea.spec.ts +445 -0
- package/examples/module-scrollarea/module-scrollarea.ts +81 -0
- package/examples/module-tabgroup/module-tabgroup.css +55 -0
- package/examples/module-tabgroup/module-tabgroup.html +269 -0
- package/examples/module-tabgroup/module-tabgroup.spec.ts +631 -0
- package/examples/module-tabgroup/module-tabgroup.ts +102 -0
- package/examples/module-toc/module-toc.css +34 -0
- package/examples/module-todo/module-todo.css +84 -0
- package/examples/module-todo/module-todo.html +92 -0
- package/examples/module-todo/module-todo.spec.ts +528 -0
- package/examples/module-todo/module-todo.ts +91 -0
- package/examples/section-hero/section-hero.css +37 -0
- package/examples/section-menu/section-menu.css +81 -0
- package/examples/server.ts +95 -0
- package/examples/test-setup.md +314 -0
- package/index.dev.js +1688 -0
- package/index.dev.ts +127 -0
- package/index.js +3 -0
- package/index.js.map +42 -0
- package/index.ts +127 -0
- package/package.json +64 -0
- package/playwright.config.ts +31 -0
- package/server/BUILD_SYSTEM.md +428 -0
- package/server/SERVER.md +286 -0
- package/server/build.ts +91 -0
- package/server/config.ts +130 -0
- package/server/effects/api.ts +28 -0
- package/server/effects/css.ts +31 -0
- package/server/effects/examples.ts +109 -0
- package/server/effects/js.ts +32 -0
- package/server/effects/menu.ts +34 -0
- package/server/effects/pages.ts +178 -0
- package/server/effects/service-worker.ts +57 -0
- package/server/effects/sitemap.ts +27 -0
- package/server/file-signals.ts +361 -0
- package/server/file-watcher.ts +77 -0
- package/server/io.ts +174 -0
- package/server/layout-engine.ts +470 -0
- package/server/layout-utils.ts +615 -0
- package/server/layouts/api.html +76 -0
- package/server/layouts/base.html +37 -0
- package/server/layouts/blog.html +115 -0
- package/server/layouts/example.html +104 -0
- package/server/layouts/overview.html +165 -0
- package/server/layouts/page.html +36 -0
- package/server/layouts/test.html +24 -0
- package/server/markdoc-helpers.ts +217 -0
- package/server/markdoc.config.ts +29 -0
- package/server/schema/callout.markdoc.ts +17 -0
- package/server/schema/carousel.markdoc.ts +118 -0
- package/server/schema/demo.markdoc.ts +74 -0
- package/server/schema/fence.markdoc.ts +84 -0
- package/server/schema/heading.markdoc.ts +23 -0
- package/server/schema/hero.markdoc.ts +59 -0
- package/server/schema/section.markdoc.ts +10 -0
- package/server/schema/slide.markdoc.ts +17 -0
- package/server/schema/source.markdoc.ts +53 -0
- package/server/schema/tabgroup.markdoc.ts +102 -0
- package/server/serve.ts +635 -0
- package/server/templates/README.md +352 -0
- package/server/templates/constants.ts +236 -0
- package/server/templates/fragments.ts +159 -0
- package/server/templates/hmr.ts +269 -0
- package/server/templates/menu.ts +33 -0
- package/server/templates/performance-hints.ts +94 -0
- package/server/templates/service-worker.ts +403 -0
- package/server/templates/sitemap.ts +57 -0
- package/server/templates/toc.ts +41 -0
- package/server/templates/utils.ts +378 -0
- package/src/component.ts +215 -0
- package/src/context.ts +156 -0
- package/src/effects/attribute.ts +82 -0
- package/src/effects/class.ts +28 -0
- package/src/effects/event.ts +67 -0
- package/src/effects/html.ts +60 -0
- package/src/effects/method.ts +57 -0
- package/src/effects/pass.ts +103 -0
- package/src/effects/property.ts +57 -0
- package/src/effects/style.ts +34 -0
- package/src/effects/text.ts +28 -0
- package/src/effects.ts +412 -0
- package/src/errors.ts +160 -0
- package/src/parsers/boolean.ts +14 -0
- package/src/parsers/json.ts +33 -0
- package/src/parsers/number.ts +55 -0
- package/src/parsers/string.ts +32 -0
- package/src/parsers.ts +90 -0
- package/src/scheduler.ts +47 -0
- package/src/signals/collection.ts +253 -0
- package/src/signals/sensor.ts +131 -0
- package/src/ui.ts +236 -0
- package/src/util.ts +187 -0
- package/tsconfig.json +34 -0
- package/types/examples/basic-button/basic-button.d.ts +16 -0
- package/types/examples/basic-hello/basic-hello.d.ts +18 -0
- package/types/index.d.ts +27 -0
- package/types/index.dev.d.ts +27 -0
- package/types/src/collection.d.ts +27 -0
- package/types/src/component.d.ts +32 -0
- package/types/src/context.d.ts +85 -0
- package/types/src/effects/attribute.d.ts +23 -0
- package/types/src/effects/callMethod.d.ts +23 -0
- package/types/src/effects/class.d.ts +13 -0
- package/types/src/effects/dangerouslySetInnerHTML.d.ts +18 -0
- package/types/src/effects/event.d.ts +18 -0
- package/types/src/effects/html.d.ts +17 -0
- package/types/src/effects/method.d.ts +22 -0
- package/types/src/effects/pass.d.ts +18 -0
- package/types/src/effects/property.d.ts +22 -0
- package/types/src/effects/setAttribute.d.ts +24 -0
- package/types/src/effects/setProperty.d.ts +23 -0
- package/types/src/effects/setStyle.d.ts +14 -0
- package/types/src/effects/setText.d.ts +13 -0
- package/types/src/effects/style.d.ts +13 -0
- package/types/src/effects/text.d.ts +12 -0
- package/types/src/effects/toggleClass.d.ts +14 -0
- package/types/src/effects.d.ts +153 -0
- package/types/src/errors.d.ts +99 -0
- package/types/src/events.d.ts +27 -0
- package/types/src/extractors.d.ts +23 -0
- package/types/src/parsers/boolean.d.ts +10 -0
- package/types/src/parsers/json.d.ts +13 -0
- package/types/src/parsers/number.d.ts +21 -0
- package/types/src/parsers/string.d.ts +19 -0
- package/types/src/parsers.d.ts +41 -0
- package/types/src/scheduler.d.ts +11 -0
- package/types/src/sensor.d.ts +27 -0
- package/types/src/signals/collection.d.ts +32 -0
- package/types/src/signals/sensor.d.ts +27 -0
- package/types/src/ui.d.ts +37 -0
- package/types/src/util.d.ts +65 -0
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONTENT_MARKER,
|
|
3
|
+
LAYOUT_PATHS,
|
|
4
|
+
LAYOUTS_DIR,
|
|
5
|
+
type LayoutConfig,
|
|
6
|
+
ROUTE_LAYOUT_MAP,
|
|
7
|
+
} from './config'
|
|
8
|
+
import { fileExists, getFileContent, getFilePath, writeFileSafe } from './io'
|
|
9
|
+
import { DEFAULT_LAYOUTS, LayoutEngine } from './layout-engine'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Layout development utilities
|
|
13
|
+
*/
|
|
14
|
+
export class LayoutUtils {
|
|
15
|
+
/**
|
|
16
|
+
* Validate all layout files exist and are readable
|
|
17
|
+
*/
|
|
18
|
+
static async validateLayouts(): Promise<{
|
|
19
|
+
valid: boolean
|
|
20
|
+
errors: string[]
|
|
21
|
+
}> {
|
|
22
|
+
const errors: string[] = []
|
|
23
|
+
|
|
24
|
+
for (const [name, path] of Object.entries(LAYOUT_PATHS)) {
|
|
25
|
+
if (!fileExists(path)) {
|
|
26
|
+
errors.push(`Layout file not found: ${name} at ${path}`)
|
|
27
|
+
continue
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
await getFileContent(path)
|
|
32
|
+
} catch (error) {
|
|
33
|
+
errors.push(`Cannot read layout file: ${name} at ${path} - ${error}`)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
valid: errors.length === 0,
|
|
39
|
+
errors,
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Generate a layout template with common structure
|
|
45
|
+
*/
|
|
46
|
+
static generateLayoutTemplate(
|
|
47
|
+
name: string,
|
|
48
|
+
options: {
|
|
49
|
+
title?: string
|
|
50
|
+
hasHeader?: boolean
|
|
51
|
+
hasFooter?: boolean
|
|
52
|
+
hasNavigation?: boolean
|
|
53
|
+
customCSS?: string
|
|
54
|
+
customJS?: string
|
|
55
|
+
} = {},
|
|
56
|
+
): string {
|
|
57
|
+
const {
|
|
58
|
+
title = `{{ title }} – Le Truc`,
|
|
59
|
+
hasHeader = true,
|
|
60
|
+
hasFooter = true,
|
|
61
|
+
hasNavigation = false,
|
|
62
|
+
customCSS = '',
|
|
63
|
+
customJS = '',
|
|
64
|
+
} = options
|
|
65
|
+
|
|
66
|
+
return `<!doctype html>
|
|
67
|
+
<html lang="en">
|
|
68
|
+
<head>
|
|
69
|
+
<meta charset="UTF-8" />
|
|
70
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
71
|
+
<title>${title}</title>
|
|
72
|
+
<meta name="description" content="{{ description }}" />
|
|
73
|
+
<link
|
|
74
|
+
rel="preload"
|
|
75
|
+
href="{{ base-path }}assets/main.css?v={{ css-hash }}"
|
|
76
|
+
as="style"
|
|
77
|
+
/>
|
|
78
|
+
<link
|
|
79
|
+
rel="modulepreload"
|
|
80
|
+
href="{{ base-path }}assets/main.js?v={{ js-hash }}"
|
|
81
|
+
/>
|
|
82
|
+
<link
|
|
83
|
+
rel="stylesheet"
|
|
84
|
+
href="{{ base-path }}assets/main.css?v={{ css-hash }}"
|
|
85
|
+
/>
|
|
86
|
+
<script
|
|
87
|
+
type="module"
|
|
88
|
+
src="{{ base-path }}assets/main.js?v={{ js-hash }}"
|
|
89
|
+
></script>${
|
|
90
|
+
customCSS
|
|
91
|
+
? `
|
|
92
|
+
<style>
|
|
93
|
+
${customCSS}
|
|
94
|
+
</style>`
|
|
95
|
+
: ''
|
|
96
|
+
}
|
|
97
|
+
</head>
|
|
98
|
+
<body class="{{ section }} ${name}-page">${
|
|
99
|
+
hasHeader
|
|
100
|
+
? `
|
|
101
|
+
<context-router>
|
|
102
|
+
<header class="content-grid">
|
|
103
|
+
<a href="#main" class="skiplink visually-hidden">
|
|
104
|
+
Skip to main content
|
|
105
|
+
</a>
|
|
106
|
+
<h1 class="content">Le Truc <small>Version 0.15.0</small></h1>${
|
|
107
|
+
hasNavigation
|
|
108
|
+
? `
|
|
109
|
+
{{ include 'menu.html' }}`
|
|
110
|
+
: ''
|
|
111
|
+
}
|
|
112
|
+
<card-callout class="content danger" hidden>
|
|
113
|
+
<p class="error" role="alert" aria-live="assertive"></p>
|
|
114
|
+
</card-callout>
|
|
115
|
+
</header>`
|
|
116
|
+
: ''
|
|
117
|
+
}
|
|
118
|
+
<main id="main" class="content-grid">
|
|
119
|
+
<div class="content">
|
|
120
|
+
{{ content }}
|
|
121
|
+
</div>
|
|
122
|
+
</main>${
|
|
123
|
+
hasFooter
|
|
124
|
+
? `
|
|
125
|
+
<footer class="content-grid">
|
|
126
|
+
<div class="content">
|
|
127
|
+
<h2 class="visually-hidden">Footer</h2>
|
|
128
|
+
<p>© 2024 – 2025 Zeix AG</p>
|
|
129
|
+
</div>
|
|
130
|
+
</footer>`
|
|
131
|
+
: ''
|
|
132
|
+
}${
|
|
133
|
+
hasHeader
|
|
134
|
+
? `
|
|
135
|
+
</context-router>`
|
|
136
|
+
: ''
|
|
137
|
+
}${
|
|
138
|
+
customJS
|
|
139
|
+
? `
|
|
140
|
+
<script>
|
|
141
|
+
${customJS}
|
|
142
|
+
</script>`
|
|
143
|
+
: ''
|
|
144
|
+
}
|
|
145
|
+
</body>
|
|
146
|
+
</html>`
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Create a new layout file
|
|
151
|
+
*/
|
|
152
|
+
static async createLayout(
|
|
153
|
+
name: string,
|
|
154
|
+
template?: string,
|
|
155
|
+
options?: Parameters<typeof LayoutUtils.generateLayoutTemplate>[1],
|
|
156
|
+
): Promise<void> {
|
|
157
|
+
const layoutPath = getFilePath(LAYOUTS_DIR, `${name}.html`)
|
|
158
|
+
|
|
159
|
+
if (fileExists(layoutPath)) {
|
|
160
|
+
throw new Error(`Layout ${name} already exists at ${layoutPath}`)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const content = template || this.generateLayoutTemplate(name, options)
|
|
164
|
+
await writeFileSafe(layoutPath, content)
|
|
165
|
+
|
|
166
|
+
console.log(`✅ Created layout: ${name} at ${layoutPath}`)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Extract template variables from layout content
|
|
171
|
+
*/
|
|
172
|
+
static extractTemplateVariables(content: string): string[] {
|
|
173
|
+
const variables = new Set<string>()
|
|
174
|
+
const regex = /{{\s*([\w\-]+)\s*}}/g
|
|
175
|
+
let match: RegExpExecArray | null
|
|
176
|
+
|
|
177
|
+
while ((match = regex.exec(content)) !== null) {
|
|
178
|
+
const variable = match[1].trim()
|
|
179
|
+
if (variable !== 'content') {
|
|
180
|
+
// Exclude the main content variable
|
|
181
|
+
variables.add(variable)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return Array.from(variables).sort()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Analyze layout usage across routes
|
|
190
|
+
*/
|
|
191
|
+
static analyzeLayoutUsage(): Record<string, string[]> {
|
|
192
|
+
const usage: Record<string, string[]> = {}
|
|
193
|
+
|
|
194
|
+
for (const [route, layout] of Object.entries(ROUTE_LAYOUT_MAP)) {
|
|
195
|
+
if (!usage[layout]) {
|
|
196
|
+
usage[layout] = []
|
|
197
|
+
}
|
|
198
|
+
usage[layout].push(route)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return usage
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Generate layout configuration for a new layout
|
|
206
|
+
*/
|
|
207
|
+
static generateLayoutConfig(
|
|
208
|
+
name: string,
|
|
209
|
+
options: {
|
|
210
|
+
type?: 'simple' | 'template'
|
|
211
|
+
contentMarker?: string
|
|
212
|
+
defaultContext?: Record<string, string>
|
|
213
|
+
} = {},
|
|
214
|
+
): LayoutConfig {
|
|
215
|
+
return {
|
|
216
|
+
name,
|
|
217
|
+
path: getFilePath(LAYOUTS_DIR, `${name}.html`),
|
|
218
|
+
type: options.type || 'template',
|
|
219
|
+
contentMarker: options.contentMarker || CONTENT_MARKER,
|
|
220
|
+
defaultContext: options.defaultContext,
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Preview layout with sample content
|
|
226
|
+
*/
|
|
227
|
+
static async previewLayout(
|
|
228
|
+
name: string,
|
|
229
|
+
sampleContent: string = '<h1>Sample Content</h1><p>This is a preview of your layout.</p>',
|
|
230
|
+
context: Record<string, string> = {},
|
|
231
|
+
): Promise<string> {
|
|
232
|
+
const engine = new LayoutEngine(DEFAULT_LAYOUTS)
|
|
233
|
+
|
|
234
|
+
const defaultContext = {
|
|
235
|
+
title: 'Layout Preview',
|
|
236
|
+
description: 'Preview of layout with sample content',
|
|
237
|
+
section: 'preview',
|
|
238
|
+
'base-path': '',
|
|
239
|
+
'css-hash': 'preview',
|
|
240
|
+
'js-hash': 'preview',
|
|
241
|
+
...context,
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return engine.renderWithLayout(name, sampleContent, defaultContext)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Check for unused template variables in layouts
|
|
249
|
+
*/
|
|
250
|
+
static async findUnusedVariables(): Promise<Record<string, string[]>> {
|
|
251
|
+
const unused: Record<string, string[]> = {}
|
|
252
|
+
|
|
253
|
+
for (const [name, path] of Object.entries(LAYOUT_PATHS)) {
|
|
254
|
+
if (!fileExists(path)) continue
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
const content = await getFileContent(path)
|
|
258
|
+
const variables = this.extractTemplateVariables(content)
|
|
259
|
+
|
|
260
|
+
// Common variables that are always available
|
|
261
|
+
const commonVars = new Set([
|
|
262
|
+
'title',
|
|
263
|
+
'description',
|
|
264
|
+
'section',
|
|
265
|
+
'base-path',
|
|
266
|
+
'css-hash',
|
|
267
|
+
'js-hash',
|
|
268
|
+
'content',
|
|
269
|
+
])
|
|
270
|
+
|
|
271
|
+
const potentiallyUnused = variables.filter(v => !commonVars.has(v))
|
|
272
|
+
if (potentiallyUnused.length > 0) {
|
|
273
|
+
unused[name] = potentiallyUnused
|
|
274
|
+
}
|
|
275
|
+
} catch (error) {
|
|
276
|
+
console.warn(`Could not analyze ${name}: ${error}`)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return unused
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Generate route test URLs for layout testing
|
|
285
|
+
*/
|
|
286
|
+
static generateTestUrls(): Record<string, string[]> {
|
|
287
|
+
const testUrls: Record<string, string[]> = {}
|
|
288
|
+
|
|
289
|
+
for (const [route, layout] of Object.entries(ROUTE_LAYOUT_MAP)) {
|
|
290
|
+
if (!testUrls[layout]) {
|
|
291
|
+
testUrls[layout] = []
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Generate sample URLs based on route patterns
|
|
295
|
+
if (route.includes('{')) {
|
|
296
|
+
// Dynamic route - generate sample
|
|
297
|
+
const sampleUrl = route
|
|
298
|
+
.replace('{component}', 'sample-component')
|
|
299
|
+
.replace('{example}', 'basic-example')
|
|
300
|
+
.replace('{id}', 'sample-item')
|
|
301
|
+
.replace('{article}', 'sample-article')
|
|
302
|
+
testUrls[layout].push(sampleUrl + 'sample.html')
|
|
303
|
+
} else {
|
|
304
|
+
// Static route
|
|
305
|
+
testUrls[layout].push(route + (route.endsWith('/') ? 'index.html' : ''))
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return testUrls
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Lint layout files for common issues
|
|
314
|
+
*/
|
|
315
|
+
static async lintLayouts(): Promise<
|
|
316
|
+
{
|
|
317
|
+
layout: string
|
|
318
|
+
issues: Array<{ type: 'error' | 'warning' | 'info'; message: string }>
|
|
319
|
+
}[]
|
|
320
|
+
> {
|
|
321
|
+
const results: {
|
|
322
|
+
layout: string
|
|
323
|
+
issues: Array<{ type: 'error' | 'warning' | 'info'; message: string }>
|
|
324
|
+
}[] = []
|
|
325
|
+
|
|
326
|
+
for (const [name, path] of Object.entries(LAYOUT_PATHS)) {
|
|
327
|
+
const issues: Array<{
|
|
328
|
+
type: 'error' | 'warning' | 'info'
|
|
329
|
+
message: string
|
|
330
|
+
}> = []
|
|
331
|
+
|
|
332
|
+
if (!fileExists(path)) {
|
|
333
|
+
issues.push({
|
|
334
|
+
type: 'error',
|
|
335
|
+
message: `Layout file not found: ${path}`,
|
|
336
|
+
})
|
|
337
|
+
results.push({ layout: name, issues })
|
|
338
|
+
continue
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
try {
|
|
342
|
+
const content = await getFileContent(path)
|
|
343
|
+
|
|
344
|
+
// Check for required elements
|
|
345
|
+
if (!content.includes('<!doctype html>')) {
|
|
346
|
+
issues.push({
|
|
347
|
+
type: 'warning',
|
|
348
|
+
message: 'Missing DOCTYPE declaration',
|
|
349
|
+
})
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (!content.includes('<title>')) {
|
|
353
|
+
issues.push({ type: 'warning', message: 'Missing title element' })
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (!content.includes('{{ content }}')) {
|
|
357
|
+
issues.push({
|
|
358
|
+
type: 'error',
|
|
359
|
+
message: 'Missing {{ content }} placeholder',
|
|
360
|
+
})
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (!content.includes('charset="UTF-8"')) {
|
|
364
|
+
issues.push({
|
|
365
|
+
type: 'warning',
|
|
366
|
+
message: 'Missing or incorrect charset declaration',
|
|
367
|
+
})
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (!content.includes('viewport')) {
|
|
371
|
+
issues.push({ type: 'warning', message: 'Missing viewport meta tag' })
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Check for accessibility
|
|
375
|
+
if (!content.includes('lang="')) {
|
|
376
|
+
issues.push({
|
|
377
|
+
type: 'warning',
|
|
378
|
+
message: 'Missing lang attribute on html element',
|
|
379
|
+
})
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (
|
|
383
|
+
content.includes('<a href="#main"')
|
|
384
|
+
&& !content.includes('id="main"')
|
|
385
|
+
) {
|
|
386
|
+
issues.push({
|
|
387
|
+
type: 'warning',
|
|
388
|
+
message: 'Skip link target not found',
|
|
389
|
+
})
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Check for potential issues
|
|
393
|
+
if (content.includes('{{') && content.includes('}}')) {
|
|
394
|
+
const variables = this.extractTemplateVariables(content)
|
|
395
|
+
if (variables.length === 0) {
|
|
396
|
+
issues.push({
|
|
397
|
+
type: 'info',
|
|
398
|
+
message: 'No template variables found',
|
|
399
|
+
})
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
results.push({ layout: name, issues })
|
|
404
|
+
} catch (error) {
|
|
405
|
+
issues.push({
|
|
406
|
+
type: 'error',
|
|
407
|
+
message: `Cannot read layout file: ${error}`,
|
|
408
|
+
})
|
|
409
|
+
results.push({ layout: name, issues })
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return results
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Generate development report for layouts
|
|
418
|
+
*/
|
|
419
|
+
static async generateReport(): Promise<string> {
|
|
420
|
+
const validation = await this.validateLayouts()
|
|
421
|
+
const usage = this.analyzeLayoutUsage()
|
|
422
|
+
const testUrls = this.generateTestUrls()
|
|
423
|
+
const lintResults = await this.lintLayouts()
|
|
424
|
+
const unused = await this.findUnusedVariables()
|
|
425
|
+
|
|
426
|
+
let report = '# Layout System Report\n\n'
|
|
427
|
+
|
|
428
|
+
// Validation section
|
|
429
|
+
report += '## Layout Validation\n\n'
|
|
430
|
+
if (validation.valid) {
|
|
431
|
+
report += '✅ All layouts are valid and readable\n\n'
|
|
432
|
+
} else {
|
|
433
|
+
report += '❌ Layout validation issues found:\n\n'
|
|
434
|
+
for (const error of validation.errors) {
|
|
435
|
+
report += `- ${error}\n`
|
|
436
|
+
}
|
|
437
|
+
report += '\n'
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Usage analysis
|
|
441
|
+
report += '## Layout Usage\n\n'
|
|
442
|
+
for (const [layout, routes] of Object.entries(usage)) {
|
|
443
|
+
report += `### ${layout}\n`
|
|
444
|
+
report += `Routes: ${routes.length}\n`
|
|
445
|
+
for (const route of routes) {
|
|
446
|
+
report += `- \`${route}\`\n`
|
|
447
|
+
}
|
|
448
|
+
report += '\n'
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Test URLs
|
|
452
|
+
report += '## Test URLs\n\n'
|
|
453
|
+
for (const [layout, urls] of Object.entries(testUrls)) {
|
|
454
|
+
report += `### ${layout}\n`
|
|
455
|
+
for (const url of urls) {
|
|
456
|
+
report += `- http://localhost:5000${url}\n`
|
|
457
|
+
}
|
|
458
|
+
report += '\n'
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Lint results
|
|
462
|
+
report += '## Layout Linting\n\n'
|
|
463
|
+
for (const result of lintResults) {
|
|
464
|
+
if (result.issues.length === 0) {
|
|
465
|
+
report += `✅ **${result.layout}**: No issues\n\n`
|
|
466
|
+
} else {
|
|
467
|
+
report += `### ${result.layout}\n\n`
|
|
468
|
+
for (const issue of result.issues) {
|
|
469
|
+
const icon =
|
|
470
|
+
issue.type === 'error'
|
|
471
|
+
? '❌'
|
|
472
|
+
: issue.type === 'warning'
|
|
473
|
+
? '⚠️'
|
|
474
|
+
: 'ℹ️'
|
|
475
|
+
report += `${icon} ${issue.message}\n`
|
|
476
|
+
}
|
|
477
|
+
report += '\n'
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Unused variables
|
|
482
|
+
if (Object.keys(unused).length > 0) {
|
|
483
|
+
report += '## Potentially Unused Variables\n\n'
|
|
484
|
+
for (const [layout, variables] of Object.entries(unused)) {
|
|
485
|
+
report += `### ${layout}\n`
|
|
486
|
+
for (const variable of variables) {
|
|
487
|
+
report += `- \`{{ ${variable} }}\`\n`
|
|
488
|
+
}
|
|
489
|
+
report += '\n'
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return report
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* CLI helper functions
|
|
499
|
+
*/
|
|
500
|
+
export const LayoutCLI = {
|
|
501
|
+
/**
|
|
502
|
+
* Create a new layout interactively
|
|
503
|
+
*/
|
|
504
|
+
async createInteractive(): Promise<void> {
|
|
505
|
+
console.log('🎨 Creating new layout...')
|
|
506
|
+
|
|
507
|
+
// This would typically use a CLI library like inquirer
|
|
508
|
+
// For now, just show the concept
|
|
509
|
+
const name = process.argv[3] || 'new-layout'
|
|
510
|
+
const hasHeader = true
|
|
511
|
+
const hasFooter = true
|
|
512
|
+
const hasNavigation = false
|
|
513
|
+
|
|
514
|
+
await LayoutUtils.createLayout(name, undefined, {
|
|
515
|
+
hasHeader,
|
|
516
|
+
hasFooter,
|
|
517
|
+
hasNavigation,
|
|
518
|
+
})
|
|
519
|
+
|
|
520
|
+
console.log(`
|
|
521
|
+
Next steps:
|
|
522
|
+
1. Edit the layout file: server/layouts/${name}.html
|
|
523
|
+
2. Add route mapping in server/config.ts
|
|
524
|
+
3. Add to DEFAULT_LAYOUTS in server/layout-engine.ts
|
|
525
|
+
4. Test with: bun server/serve.ts --mode unified
|
|
526
|
+
`)
|
|
527
|
+
},
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Validate all layouts
|
|
531
|
+
*/
|
|
532
|
+
async validate(): Promise<void> {
|
|
533
|
+
console.log('🔍 Validating layouts...')
|
|
534
|
+
const result = await LayoutUtils.validateLayouts()
|
|
535
|
+
|
|
536
|
+
if (result.valid) {
|
|
537
|
+
console.log('✅ All layouts are valid')
|
|
538
|
+
} else {
|
|
539
|
+
console.log('❌ Validation errors found:')
|
|
540
|
+
for (const error of result.errors) {
|
|
541
|
+
console.log(` ${error}`)
|
|
542
|
+
}
|
|
543
|
+
process.exit(1)
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Generate and display report
|
|
549
|
+
*/
|
|
550
|
+
async report(): Promise<void> {
|
|
551
|
+
console.log('📊 Generating layout report...')
|
|
552
|
+
const report = await LayoutUtils.generateReport()
|
|
553
|
+
console.log(report)
|
|
554
|
+
},
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Preview a layout
|
|
558
|
+
*/
|
|
559
|
+
async preview(): Promise<void> {
|
|
560
|
+
const layoutName = process.argv[3]
|
|
561
|
+
if (!layoutName) {
|
|
562
|
+
console.log('Usage: bun server/layout-utils.ts preview <layout-name>')
|
|
563
|
+
process.exit(1)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
try {
|
|
567
|
+
const html = await LayoutUtils.previewLayout(layoutName)
|
|
568
|
+
console.log('📋 Layout preview:')
|
|
569
|
+
console.log(html)
|
|
570
|
+
} catch (error) {
|
|
571
|
+
console.error('❌ Preview failed:', error)
|
|
572
|
+
process.exit(1)
|
|
573
|
+
}
|
|
574
|
+
},
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// CLI interface
|
|
578
|
+
if (import.meta.main) {
|
|
579
|
+
const command = process.argv[2]
|
|
580
|
+
|
|
581
|
+
switch (command) {
|
|
582
|
+
case 'create':
|
|
583
|
+
await LayoutCLI.createInteractive()
|
|
584
|
+
break
|
|
585
|
+
case 'validate':
|
|
586
|
+
await LayoutCLI.validate()
|
|
587
|
+
break
|
|
588
|
+
case 'report':
|
|
589
|
+
await LayoutCLI.report()
|
|
590
|
+
break
|
|
591
|
+
case 'preview':
|
|
592
|
+
await LayoutCLI.preview()
|
|
593
|
+
break
|
|
594
|
+
default:
|
|
595
|
+
console.log(`
|
|
596
|
+
Layout Utils CLI
|
|
597
|
+
|
|
598
|
+
Usage:
|
|
599
|
+
bun server/layout-utils.ts <command>
|
|
600
|
+
|
|
601
|
+
Commands:
|
|
602
|
+
create Create a new layout interactively
|
|
603
|
+
validate Validate all layout files
|
|
604
|
+
report Generate development report
|
|
605
|
+
preview Preview a layout with sample content
|
|
606
|
+
|
|
607
|
+
Examples:
|
|
608
|
+
bun server/layout-utils.ts create
|
|
609
|
+
bun server/layout-utils.ts validate
|
|
610
|
+
bun server/layout-utils.ts report
|
|
611
|
+
bun server/layout-utils.ts preview page
|
|
612
|
+
`)
|
|
613
|
+
break
|
|
614
|
+
}
|
|
615
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>{{ title }} – Le Truc API</title>
|
|
7
|
+
<meta name="description" content="{{ description }}" />
|
|
8
|
+
<link
|
|
9
|
+
rel="preload"
|
|
10
|
+
href="{{ base-path }}assets/main.css?v={{ css-hash }}"
|
|
11
|
+
as="style"
|
|
12
|
+
/>
|
|
13
|
+
<link
|
|
14
|
+
rel="modulepreload"
|
|
15
|
+
href="{{ base-path }}assets/main.js?v={{ js-hash }}"
|
|
16
|
+
/>
|
|
17
|
+
<link
|
|
18
|
+
rel="stylesheet"
|
|
19
|
+
href="{{ base-path }}assets/main.css?v={{ css-hash }}"
|
|
20
|
+
/>
|
|
21
|
+
<script
|
|
22
|
+
type="module"
|
|
23
|
+
src="{{ base-path }}assets/main.js?v={{ js-hash }}"
|
|
24
|
+
></script>
|
|
25
|
+
</head>
|
|
26
|
+
<body class="api">
|
|
27
|
+
<context-router>
|
|
28
|
+
<header class="content-grid">
|
|
29
|
+
<a href="#main" class="skiplink visually-hidden">
|
|
30
|
+
Skip to main content
|
|
31
|
+
</a>
|
|
32
|
+
<h1 class="content">Le Truc Docs <small>Version 0.15.0</small></h1>
|
|
33
|
+
{{ include 'menu.html' }}
|
|
34
|
+
<card-callout class="content danger" hidden>
|
|
35
|
+
<p class="error" role="alert" aria-live="assertive"></p>
|
|
36
|
+
</card-callout>
|
|
37
|
+
</header>
|
|
38
|
+
<div class="api-header">
|
|
39
|
+
<div class="content-grid">
|
|
40
|
+
<div class="content">
|
|
41
|
+
<nav class="api-breadcrumb" aria-label="Breadcrumb">
|
|
42
|
+
<a href="/api/">API</a> ›
|
|
43
|
+
<a href="/api/{{ api-category }}/">{{ api-category }}</a> ›
|
|
44
|
+
{{ api-name }}
|
|
45
|
+
</nav>
|
|
46
|
+
<h1 class="api-title">
|
|
47
|
+
{{ api-name }}
|
|
48
|
+
<span class="api-kind {{ api-kind }}">{{ api-kind }}</span>
|
|
49
|
+
</h1>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
<main id="main" class="content-grid">
|
|
54
|
+
<div class="content api-content">
|
|
55
|
+
<div class="grid" style="grid-template-columns: 1fr 300px; gap: 2rem;">
|
|
56
|
+
<article></article>
|
|
57
|
+
{{ content }}
|
|
58
|
+
</article>
|
|
59
|
+
<aside class="api-sidebar">
|
|
60
|
+
<nav class="api-nav" aria-label="Page contents">
|
|
61
|
+
<h3>On this page</h3>
|
|
62
|
+
{{ toc }}
|
|
63
|
+
</nav>
|
|
64
|
+
</aside>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</main>
|
|
68
|
+
<footer class="content-grid">
|
|
69
|
+
<div class="content">
|
|
70
|
+
<h2 class="visually-hidden">Footer</h2>
|
|
71
|
+
<p>© 2024 – 2025 Zeix AG</p>
|
|
72
|
+
</div>
|
|
73
|
+
</footer>
|
|
74
|
+
</context-router>
|
|
75
|
+
</body>
|
|
76
|
+
</html>
|