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