diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 7824d19..0b80947 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -33,9 +33,19 @@ jobs: - name: Build docs site run: pnpm --filter docs-site build + - name: Prepare deploy directory + run: | + cd apps/docs/dist + cp -r assets devtools/assets + cp elm.*.js devtools/ + cp style.css devtools/ 2>/dev/null || true + cp favicon.ico devtools/ 2>/dev/null || true + cp content.dat devtools/ 2>/dev/null || true + find devtools -name '*.html' -exec sed -i 's|src="/elm\.|src="/devtools/elm.|g' {} + + - uses: actions/upload-pages-artifact@v3 with: - path: apps/docs/dist + path: apps/docs/dist/devtools deploy: environment: diff --git a/apps/docs/app/Route/Index.elm b/apps/docs/app/Route/Index.elm index 1647436..83f0334 100644 --- a/apps/docs/app/Route/Index.elm +++ b/apps/docs/app/Route/Index.elm @@ -8,8 +8,8 @@ import Html import Html.Attributes as Attr import Pages.Url import PagesMsg exposing (PagesMsg) +import Route exposing (Route) import UrlPath -import Route import RouteBuilder exposing (App, StatelessRoute) import Shared import View exposing (View) @@ -98,41 +98,42 @@ viewPackageGrid = [ viewPackageCard { name = "@wolfcola/treeshake-check" , description = "CLI & library to verify packages are tree-shakeable by Rollup" - , href = "/packages/treeshake-check" + , route = Route.Packages__Slug_ { slug = "treeshake-check" } , tag = "Published" } , viewPackageCard { name = "@wolfcola/eslint-plugin-treeshake" , description = "ESLint plugin that flags tree-breaking patterns" - , href = "/packages/eslint-plugin-treeshake" + , route = Route.Packages__Slug_ { slug = "eslint-plugin-treeshake" } , tag = "Published" } , viewPackageCard { name = "@wolfcola/devtools-bridge" , description = "SDK adapter for emitting events from DaVinci, Journey, OIDC clients" - , href = "/packages/devtools-bridge" + , route = Route.Packages__Slug_ { slug = "devtools-bridge" } , tag = "Published" } , viewPackageCard { name = "@wolfcola/devtools-types" , description = "Effect Schema definitions for AuthEvent and FlowState" - , href = "/packages/devtools-types" + , route = Route.Packages__Slug_ { slug = "devtools-types" } , tag = "Published" } ] viewPackageCard : - { name : String, description : String, href : String, tag : String } + { name : String, description : String, route : Route, tag : String } -> Html.Html (PagesMsg Msg) viewPackageCard pkg = - Html.a [ Attr.class "package-card", Attr.href pkg.href ] + Route.link [ Attr.class "package-card" ] [ Html.div [ Attr.class "package-card-header" ] [ Html.h3 [] [ Html.text pkg.name ] , Html.span [ Attr.class "package-tag" ] [ Html.text pkg.tag ] ] , Html.p [] [ Html.text pkg.description ] ] + pkg.route viewQuickLinks : Html.Html (PagesMsg Msg) @@ -141,16 +142,13 @@ viewQuickLinks = [ Html.h2 [] [ Html.text "Quick Links" ] , Html.ul [] [ Html.li [] - [ Html.a [ Attr.href "/docs/getting-started" ] - [ Html.text "Installation & Setup" ] + [ Route.link [] [ Html.text "Installation & Setup" ] (Route.Docs__Slug_ { slug = "getting-started" }) ] , Html.li [] - [ Html.a [ Attr.href "/architecture" ] - [ Html.text "Architecture Overview" ] + [ Route.link [] [ Html.text "Architecture Overview" ] Route.Architecture ] , Html.li [] - [ Html.a [ Attr.href "/contributing/development-setup" ] - [ Html.text "Contributing" ] + [ Route.link [] [ Html.text "Contributing" ] (Route.Contributing__Slug_ { slug = "development-setup" }) ] ] ] diff --git a/apps/docs/app/Shared.elm b/apps/docs/app/Shared.elm index b0a5f76..b9e922f 100644 --- a/apps/docs/app/Shared.elm +++ b/apps/docs/app/Shared.elm @@ -137,7 +137,7 @@ globEntries dir section = (Decode.map2 (\title description -> { title = title - , url = "/" ++ sectionToUrlPrefix section ++ "/" ++ slug + , url = Route.baseUrl ++ sectionToUrlPrefix section ++ "/" ++ slug , section = section , excerpt = description } @@ -219,18 +219,13 @@ viewHeader sharedData model toMsg = "\u{2630}" ) ] - , Html.a - [ Attr.class "logo" - , Attr.href "/" - ] - [ Html.text "wolfcola devtools" ] + , Route.link [ Attr.class "logo" ] [ Html.text "wolfcola devtools" ] Route.Index , viewSearch sharedData model toMsg , Html.nav [ Attr.class "header-nav" ] - [ Html.a [ Attr.href "/packages/treeshake-check" ] [ Html.text "Packages" ] - , Html.a [ Attr.href "/docs/getting-started" ] [ Html.text "Guides" ] - - , Html.a [ Attr.href "/architecture" ] [ Html.text "Architecture" ] - , Html.a [ Attr.href "/contributing/development-setup" ] [ Html.text "Contributing" ] + [ Route.link [] [ Html.text "Packages" ] (Route.Packages__Slug_ { slug = "treeshake-check" }) + , Route.link [] [ Html.text "Guides" ] (Route.Docs__Slug_ { slug = "getting-started" }) + , Route.link [] [ Html.text "Architecture" ] Route.Architecture + , Route.link [] [ Html.text "Contributing" ] (Route.Contributing__Slug_ { slug = "development-setup" }) ] , Html.button [ Attr.class "theme-toggle" @@ -297,38 +292,38 @@ viewSidebar model toMsg = ) ] [ viewSidebarSection "Packages" - [ ( "/packages/treeshake-check", "treeshake-check" ) - , ( "/packages/eslint-plugin-treeshake", "eslint-plugin-treeshake" ) - , ( "/packages/devtools-bridge", "devtools-bridge" ) - , ( "/packages/devtools-types", "devtools-types" ) + [ ( Route.Packages__Slug_ { slug = "treeshake-check" }, "treeshake-check" ) + , ( Route.Packages__Slug_ { slug = "eslint-plugin-treeshake" }, "eslint-plugin-treeshake" ) + , ( Route.Packages__Slug_ { slug = "devtools-bridge" }, "devtools-bridge" ) + , ( Route.Packages__Slug_ { slug = "devtools-types" }, "devtools-types" ) ] , viewSidebarSection "Guides" - [ ( "/docs/getting-started", "Getting Started" ) - , ( "/docs/devtools-extension", "DevTools Extension" ) - , ( "/docs/vscode-extension", "VS Code Extension" ) - , ( "/docs/tree-shaking", "Tree-Shaking" ) - , ( "/docs/davinci-integration", "DaVinci Integration" ) - , ( "/docs/journey-integration", "Journey Integration" ) - , ( "/docs/oidc-integration", "Generic OIDC Integration" ) + [ ( Route.Docs__Slug_ { slug = "getting-started" }, "Getting Started" ) + , ( Route.Docs__Slug_ { slug = "devtools-extension" }, "DevTools Extension" ) + , ( Route.Docs__Slug_ { slug = "vscode-extension" }, "VS Code Extension" ) + , ( Route.Docs__Slug_ { slug = "tree-shaking" }, "Tree-Shaking" ) + , ( Route.Docs__Slug_ { slug = "davinci-integration" }, "DaVinci Integration" ) + , ( Route.Docs__Slug_ { slug = "journey-integration" }, "Journey Integration" ) + , ( Route.Docs__Slug_ { slug = "oidc-integration" }, "Generic OIDC Integration" ) ] , viewSidebarSection "Contributing" - [ ( "/contributing/development-setup", "Development Setup" ) - , ( "/contributing/repository-structure", "Repository Structure" ) - , ( "/contributing/code-style", "Code Style" ) - , ( "/contributing/release-process", "Release Process" ) + [ ( Route.Contributing__Slug_ { slug = "development-setup" }, "Development Setup" ) + , ( Route.Contributing__Slug_ { slug = "repository-structure" }, "Repository Structure" ) + , ( Route.Contributing__Slug_ { slug = "code-style" }, "Code Style" ) + , ( Route.Contributing__Slug_ { slug = "release-process" }, "Release Process" ) ] ] -viewSidebarSection : String -> List ( String, String ) -> Html msg +viewSidebarSection : String -> List ( Route, String ) -> Html msg viewSidebarSection heading links = Html.div [ Attr.class "sidebar-section" ] [ Html.h3 [ Attr.class "sidebar-heading" ] [ Html.text heading ] , Html.ul [ Attr.class "sidebar-links" ] (List.map - (\( href, label ) -> + (\( route, label ) -> Html.li [] - [ Html.a [ Attr.href href ] [ Html.text label ] ] + [ Route.link [] [ Html.text label ] route ] ) links ) diff --git a/apps/docs/app/Site.elm b/apps/docs/app/Site.elm index 7d6af63..610902c 100644 --- a/apps/docs/app/Site.elm +++ b/apps/docs/app/Site.elm @@ -8,7 +8,7 @@ import SiteConfig exposing (SiteConfig) config : SiteConfig config = - { canonicalUrl = "https://elm-pages.com" + { canonicalUrl = "https://ryanbas21.github.io/devtools" , head = head } diff --git a/apps/docs/elm-pages.config.mjs b/apps/docs/elm-pages.config.mjs index 491b738..caea559 100644 --- a/apps/docs/elm-pages.config.mjs +++ b/apps/docs/elm-pages.config.mjs @@ -2,17 +2,17 @@ import { defineConfig } from 'vite'; import adapter from 'elm-pages/adapter/netlify.js'; export default { - vite: defineConfig({}), + vite: defineConfig({ + base: '/devtools/', + }), adapter, headTagsTemplate(context) { return ` - + `; }, preloadTagForFile(file) { - // add preload directives for JS assets and font assets, etc., skip for CSS files - // this function will be called with each file that is processed by Vite, including any files in your headTagsTemplate in your config return !file.endsWith('.css'); }, }; diff --git a/apps/docs/package.json b/apps/docs/package.json index cf2de0b..753030c 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "elm-pages dev", - "build": "elm-pages build" + "build": "elm-pages build --base /devtools/" }, "devDependencies": { "elm-codegen": "^0.6.3", diff --git a/apps/docs/public/style.css b/apps/docs/public/style.css new file mode 100644 index 0000000..e2b9f49 --- /dev/null +++ b/apps/docs/public/style.css @@ -0,0 +1,449 @@ +:root { + --bg: #ffffff; + --bg-sidebar: #f8f9fa; + --text: #1a1a2e; + --text-muted: #6b7280; + --border: #e5e7eb; + --accent: #3b82f6; + --accent-hover: #2563eb; + --header-height: 56px; + --sidebar-width: 260px; + --content-max: 800px; + --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace; +} + +[data-theme='dark'] { + --bg: #0f172a; + --bg-sidebar: #1e293b; + --text: #e2e8f0; + --text-muted: #94a3b8; + --border: #334155; + --accent: #60a5fa; + --accent-hover: #93bbfd; + color-scheme: dark; + background: var(--bg); + color: var(--text); +} + +[data-theme='light'] { + background: var(--bg); + color: var(--text); +} + +[data-theme] { + min-height: 100vh; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: var(--font-sans); + color: var(--text); + background: var(--bg); + line-height: 1.6; +} + +a { + color: var(--accent); + text-decoration: none; +} +a:hover { + color: var(--accent-hover); +} + +/* Header */ +.header { + position: sticky; + top: 0; + z-index: 100; + display: flex; + align-items: center; + gap: 1rem; + height: var(--header-height); + padding: 0 1.5rem; + background: var(--bg); + border-bottom: 1px solid var(--border); +} + +.logo { + font-weight: 700; + font-size: 1.1rem; + color: var(--text); +} + +.header-nav { + display: flex; + gap: 1.5rem; + margin-left: auto; +} +.header-nav a { + color: var(--text-muted); + font-size: 0.9rem; +} +.header-nav a:hover { + color: var(--text); +} + +.sidebar-toggle { + background: none; + border: 1px solid var(--border); + border-radius: 4px; + padding: 0.25rem 0.5rem; + cursor: pointer; + color: var(--text); + font-size: 1rem; +} + +.theme-toggle { + background: none; + border: 1px solid var(--border); + border-radius: 4px; + padding: 0.25rem 0.6rem; + cursor: pointer; + color: var(--text-muted); + font-size: 0.8rem; + font-family: var(--font-sans); +} +.theme-toggle:hover { + color: var(--text); + border-color: var(--text-muted); +} + +/* Layout */ +.layout { + display: flex; + min-height: calc(100vh - var(--header-height)); +} + +/* Sidebar */ +.sidebar { + width: var(--sidebar-width); + padding: 1.5rem 1rem; + background: var(--bg-sidebar); + border-right: 1px solid var(--border); + overflow-y: auto; + position: sticky; + top: var(--header-height); + height: calc(100vh - var(--header-height)); + transition: margin-left 0.2s; +} + +.sidebar--closed { + margin-left: calc(-1 * var(--sidebar-width)); +} + +.sidebar-heading { + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-muted); + margin-bottom: 0.5rem; + margin-top: 1.5rem; +} + +.sidebar-section:first-child .sidebar-heading { + margin-top: 0; +} + +.sidebar-links { + list-style: none; + padding: 0; +} +.sidebar-links li { + margin-bottom: 0.25rem; +} + +.sidebar-links a { + display: block; + padding: 0.3rem 0.75rem; + border-radius: 4px; + color: var(--text); + font-size: 0.875rem; +} + +.sidebar-links a:hover { + background: var(--border); +} + +/* Content */ +.content { + flex: 1; + max-width: var(--content-max); + margin: 0 auto; + padding: 2rem 2rem 4rem; +} + +.content h1 { + font-size: 2rem; + margin-bottom: 0.5rem; +} +.content h2 { + font-size: 1.5rem; + margin-top: 2rem; + margin-bottom: 0.75rem; +} +.content h3 { + font-size: 1.25rem; + margin-top: 1.5rem; + margin-bottom: 0.5rem; +} +.content p { + margin-bottom: 1rem; +} +.content ul, +.content ol { + margin-bottom: 1rem; + padding-left: 1.5rem; +} +.content li { + margin-bottom: 0.25rem; +} + +.content pre { + background: var(--bg-sidebar); + border: 1px solid var(--border); + border-radius: 6px; + padding: 1rem; + overflow-x: auto; + margin-bottom: 1rem; + font-family: var(--font-mono); + font-size: 0.875rem; + line-height: 1.5; +} + +.content code { + font-family: var(--font-mono); + font-size: 0.875em; + background: var(--bg-sidebar); + padding: 0.15rem 0.35rem; + border-radius: 3px; +} + +.content pre code { + background: none; + padding: 0; +} + +/* Callouts */ +.callout { + border-left: 4px solid var(--accent); + padding: 1rem 1.25rem; + margin-bottom: 1rem; + border-radius: 0 6px 6px 0; + background: var(--bg-sidebar); +} + +.callout--warning { + border-left-color: #f59e0b; +} +.callout--info { + border-left-color: var(--accent); +} + +/* Responsive */ +@media (max-width: 768px) { + .header-nav { + display: none; + } + .sidebar { + position: fixed; + z-index: 50; + top: var(--header-height); + left: 0; + height: calc(100vh - var(--header-height)); + } + .sidebar--closed { + margin-left: calc(-1 * var(--sidebar-width)); + } + .content { + padding: 1.5rem 1rem 3rem; + } +} + +/* Landing Page */ +.hero { + text-align: center; + padding: 3rem 0 2rem; +} +.hero h1 { + font-size: 2.5rem; +} +.hero-subtitle { + font-size: 1.2rem; + color: var(--text-muted); + margin-top: 0.5rem; +} + +.package-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 1rem; + margin: 2rem 0; +} + +.package-card { + display: block; + padding: 1.25rem; + border: 1px solid var(--border); + border-radius: 8px; + color: var(--text); + transition: border-color 0.15s; +} + +.package-card:hover { + border-color: var(--accent); +} + +.package-card-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.5rem; +} + +.package-card h3 { + font-size: 1rem; +} + +.package-tag { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.05em; + padding: 0.15rem 0.5rem; + border-radius: 999px; + background: var(--accent); + color: white; +} + +.package-card p { + font-size: 0.875rem; + color: var(--text-muted); + margin: 0; +} + +.quick-links { + margin-top: 3rem; +} +.quick-links h2 { + margin-bottom: 1rem; +} + +/* Architecture */ +/* Search */ +.search-wrapper { + position: relative; + margin-left: 1rem; +} +.search-input { + width: 220px; + padding: 0.4rem 0.75rem; + border: 1px solid var(--border); + border-radius: 6px; + background: var(--bg); + color: var(--text); + font-size: 0.875rem; + font-family: var(--font-sans); +} +.search-input:focus { + outline: none; + border-color: var(--accent); +} +.search-results { + position: absolute; + top: 100%; + left: 0; + right: 0; + margin-top: 0.25rem; + background: var(--bg); + border: 1px solid var(--border); + border-radius: 6px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + max-height: 300px; + overflow-y: auto; + z-index: 200; +} +.search-result { + display: flex; + flex-direction: column; + padding: 0.5rem 0.75rem; + border-bottom: 1px solid var(--border); + color: var(--text); +} +.search-result:last-child { + border-bottom: none; +} +.search-result:hover { + background: var(--bg-sidebar); +} +.search-result-section { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-muted); +} +.search-result-title { + font-size: 0.875rem; +} + +.architecture-diagram { + display: flex; + justify-content: center; + margin: 2rem 0; + padding: 1.5rem; + background: var(--bg-sidebar); + border-radius: 8px; + border: 1px solid var(--border); +} + +/* Prism Syntax Highlighting */ +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: var(--text-muted); + font-style: italic; +} +.token.punctuation { + color: var(--text); +} +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #e06c75; +} +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #98c379; +} +.token.operator, +.token.entity, +.token.url { + color: #56b6c2; +} +.token.atrule, +.token.attr-value, +.token.keyword { + color: #c678dd; +} +.token.function, +.token.class-name { + color: #61afef; +} +.token.regex, +.token.important, +.token.variable { + color: #d19a66; +} diff --git a/apps/docs/src/MarkdownRenderer.elm b/apps/docs/src/MarkdownRenderer.elm index 478ca07..d34440e 100644 --- a/apps/docs/src/MarkdownRenderer.elm +++ b/apps/docs/src/MarkdownRenderer.elm @@ -15,6 +15,7 @@ import Html.Attributes as Attr import Markdown.Block as Block exposing (ListItem) import Markdown.Html import Markdown.Renderer exposing (Renderer) +import Route {-| The custom renderer for documentation pages. @@ -94,16 +95,24 @@ viewHeading { level, rawText, children } = viewLink : { title : Maybe String, destination : String } -> List (Html msg) -> Html msg viewLink link children = + let + resolvedHref = + if String.startsWith "/" link.destination then + String.dropRight 1 Route.baseUrl ++ link.destination + + else + link.destination + in case link.title of Just title -> Html.a - [ Attr.href link.destination + [ Attr.href resolvedHref , Attr.title title ] children Nothing -> - Html.a [ Attr.href link.destination ] children + Html.a [ Attr.href resolvedHref ] children