From 2cf2198b3ddbd0146b3c0252844e1f9e88743b6f Mon Sep 17 00:00:00 2001 From: marle3003 Date: Thu, 5 Feb 2026 18:25:19 +0100 Subject: [PATCH 01/75] add CLI short parameters -v and -h fix: cleanup GIT repository on exit update welcome documentation --- cmd/mokapi/main_test.go | 21 +++++++++++ config/dynamic/provider/git/git.go | 14 ++++---- config/dynamic/provider/git/git_test.go | 48 +++++++++++++++++++++++-- docs/guides/get-started/welcome.md | 10 ++++++ pkg/cli/command.go | 4 +-- webui/src/assets/vars.css | 6 ++++ webui/src/views/DocsView.vue | 4 +-- 7 files changed, 94 insertions(+), 13 deletions(-) diff --git a/cmd/mokapi/main_test.go b/cmd/mokapi/main_test.go index 08e8384b0..37e75f65a 100644 --- a/cmd/mokapi/main_test.go +++ b/cmd/mokapi/main_test.go @@ -23,6 +23,27 @@ func TestMain_Skeleton(t *testing.T) { require.Equal(t, "1.0\n", out) }, }, + { + name: "version short", + args: []string{"-v"}, + test: func(t *testing.T, out string) { + require.Equal(t, "1.0\n", out) + }, + }, + { + name: "help", + args: []string{"--help"}, + test: func(t *testing.T, out string) { + require.Contains(t, out, "Mokapi is an easy, modern and flexible API mocking tool using Go") + }, + }, + { + name: "help short", + args: []string{"-h"}, + test: func(t *testing.T, out string) { + require.Contains(t, out, "Mokapi is an easy, modern and flexible API mocking tool using Go") + }, + }, { name: "generate-cli-skeleton", args: []string{"--generate-cli-skeleton"}, diff --git a/config/dynamic/provider/git/git.go b/config/dynamic/provider/git/git.go index 6474d7d90..8bec16815 100644 --- a/config/dynamic/provider/git/git.go +++ b/config/dynamic/provider/git/git.go @@ -140,17 +140,20 @@ func (p *Provider) initRepository(r *repository, ch chan dynamic.ConfigEvent, po r.repo, err = git.PlainClone(r.localPath, false, options) if err != nil { + p.cleanup(r) return fmt.Errorf("unable to clone git %q: %v", r.repoUrl, err) } r.wt, err = r.repo.Worktree() if err != nil { + p.cleanup(r) return fmt.Errorf("unable to get git worktree: %v", err.Error()) } r.pullOptions = &git.PullOptions{SingleBranch: true, Depth: 1} ref, err := r.repo.Head() if err != nil { + p.cleanup(r) return fmt.Errorf("unable to get git head: %w", err) } r.hash = ref.Hash() @@ -166,6 +169,7 @@ func (p *Provider) initRepository(r *repository, ch chan dynamic.ConfigEvent, po for { select { case <-ctx.Done(): + p.cleanup(r) return case e := <-chFile: path := e.Name @@ -196,12 +200,10 @@ func (p *Provider) startFileProvider(dir string, ch chan dynamic.ConfigEvent, po } } -func (p *Provider) cleanup() { - for _, repo := range p.repositories { - err := os.RemoveAll(repo.localPath) - if err != nil { - log.Debugf("unable to remove temp dir %q: %v", repo.localPath, err.Error()) - } +func (p *Provider) cleanup(r *repository) { + err := os.RemoveAll(r.localPath) + if err != nil { + log.Debugf("unable to remove temp dir %q: %v", r.localPath, err.Error()) } } diff --git a/config/dynamic/provider/git/git_test.go b/config/dynamic/provider/git/git_test.go index 5fba0a36a..2484190f9 100644 --- a/config/dynamic/provider/git/git_test.go +++ b/config/dynamic/provider/git/git_test.go @@ -2,9 +2,6 @@ package git import ( "context" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing/object" - "github.com/stretchr/testify/require" "mokapi/config/dynamic" "mokapi/config/static" "mokapi/safe" @@ -14,6 +11,10 @@ import ( "strings" "testing" "time" + + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/object" + "github.com/stretchr/testify/require" ) var gitFiles = map[string]struct{}{ @@ -225,6 +226,33 @@ Stop: require.Len(t, files, 1) } +func TestGitFileUpdate(t *testing.T) { + repo := newGitRepo(t, t.Name()) + repo.commit(t, "foo.txt", "foo") + + g := New(static.GitProvider{Urls: []string{repo.url.String()}, PullInterval: "3s"}) + p := safe.NewPool(context.Background()) + defer p.Stop() + + ch := make(chan dynamic.ConfigEvent) + defer close(ch) + + err := g.Start(ch, p) + require.NoError(t, err) + + // wait init + _ = wait(ch, 3*time.Second) + + repo.commit(t, "foo.txt", "bar") + + // git deletes the file first + e := wait(ch, 5*time.Second) + require.Equal(t, dynamic.Delete, e.Event) + e = wait(ch, 5*time.Second) + require.NotNil(t, e.Config) + require.Equal(t, []byte("bar"), e.Config.Raw) +} + // go-git requires git installed for file:// repositories func testGitSimpleUrl(t *testing.T) { repo := newGitRepo(t, t.Name()) @@ -330,3 +358,17 @@ func (g *gitTestRepo) commit(t *testing.T, file, content string) { _, err = w.Commit("added "+file, &git.CommitOptions{Author: &object.Signature{When: ts}}) require.NoError(t, err) } + +func wait(ch chan dynamic.ConfigEvent, timeout time.Duration) *dynamic.ConfigEvent { + chTimeout := time.After(timeout) +Stop: + for { + select { + case <-chTimeout: + break Stop + case e := <-ch: + return &e + } + } + return nil +} diff --git a/docs/guides/get-started/welcome.md b/docs/guides/get-started/welcome.md index db62c3d76..6a187bdcb 100644 --- a/docs/guides/get-started/welcome.md +++ b/docs/guides/get-started/welcome.md @@ -27,6 +27,8 @@ cards: Welcome to Mokapi! Mokapi is a powerful, flexible platform for building, testing, and monitoring API-driven applications. This guide will help you quickly understand how to get started with Mokapi and introduce you to its key features. +> *Mokapi is your always-on API contract guardian — lightweight, transparent, and spec-driven.* + ## Build Better Software with Mokapi Modern applications rely on dozens of APIs—many of them outside your control. That creates friction: slow testing, fragile pipelines, and blocked teams. @@ -56,6 +58,14 @@ Mokapi offers several key benefits: -

Rapid API Prototyping:
Use Mokapi for fast prototyping of new APIs and services. Mock APIs before they’re built or deployed to ensure that your development teams can begin their work immediately, even in the absence of a fully functional backend.

-

Collaborative API Development:
Mokapi enables teams to collaborate efficiently by providing an easily accessible testing environment. Developers, QA testers, and product teams can work together on mocking and validating APIs in real time.

+## Data Privacy and Security + +Mokapi is designed as a local, offline-first tool. Your data stays on your machine and under your control at all times. + +Mokapi does not require you to create an account or log in. There is no user authentication, tracking, or identity management involved. + +Mokapi does not connect to any cloud services and does not sync your data anywhere. API specifications, mock configurations, requests, responses, and generated data remain local unless you explicitly choose to share them yourself. + ## Explore how you can mock your APIs with Mokapi Whether you are mocking APIs for local testing or validating event-driven systems, Mokapi makes the process seamless and efficient. With Mokapi’s powerful tools and integrations, you can accelerate your development cycle, reduce errors, and improve the quality of your APIs. diff --git a/pkg/cli/command.go b/pkg/cli/command.go index 1f39c60d0..168c28106 100644 --- a/pkg/cli/command.go +++ b/pkg/cli/command.go @@ -104,10 +104,10 @@ func (c *Command) Flags() *FlagSet { }, } - c.Flags().Bool("help", false, FlagDoc{Short: "Show help information and exit"}) + c.Flags().BoolShort("help", "h", false, FlagDoc{Short: "Show help information and exit"}) if c.Version != "" { - c.Flags().Bool("version", false, FlagDoc{Short: "Show version information and exit"}) + c.Flags().BoolShort("version", "v", false, FlagDoc{Short: "Show version information and exit"}) } } return c.flags diff --git a/webui/src/assets/vars.css b/webui/src/assets/vars.css index 5af3b12ad..cc39d75f6 100644 --- a/webui/src/assets/vars.css +++ b/webui/src/assets/vars.css @@ -106,6 +106,9 @@ --code-tab-border-color: #3c424b; --code-control-color-active: #eabaabff; + --blockquote-border-color: #eabaabff; + --blockquote-background-color: var(--color-background-soft); + --footer-background: #282b33; } @@ -188,5 +191,8 @@ --code-tab-border-color: #3c424b; --code-control-color-active: rgb(8, 109, 215); + --blockquote-border-color: rgb(8, 109, 215); + --blockquote-background-color: #f8f9fa; + --footer-background: rgb(244 244 246); } \ No newline at end of file diff --git a/webui/src/views/DocsView.vue b/webui/src/views/DocsView.vue index d9a17953f..b79e287bb 100644 --- a/webui/src/views/DocsView.vue +++ b/webui/src/views/DocsView.vue @@ -430,9 +430,9 @@ blockquote { margin-top: 2rem; max-width: 700px; padding: 1.5em 2em 1.5em 2em; - border-left: 4px solid #eabaabff; + border-left: 4px solid var(--blockquote-border-color); position: relative; - background: var(--color-background-soft); + background-color: var(--blockquote-background-color); } blockquote span:before { content: '- ' From 2eac26171ee43583a9ec25d6d92ee9cc7c7ff957 Mon Sep 17 00:00:00 2001 From: maesi Date: Thu, 5 Feb 2026 18:48:03 +0100 Subject: [PATCH 02/75] fix help flag with shorthand flags --- pkg/cli/docs_test.go | 18 +++++++++--------- pkg/cli/help.go | 6 ++++++ pkg/cli/help_test.go | 4 ++-- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/pkg/cli/docs_test.go b/pkg/cli/docs_test.go index b5c1a04c1..db63b027b 100644 --- a/pkg/cli/docs_test.go +++ b/pkg/cli/docs_test.go @@ -53,9 +53,9 @@ description: A complete list of all Foo flags, with descriptions, defaults, and Show help information and exit -| Flag | Env | Type | Default | -|------|------|:----:|:-------:| -| --help | HELP | bool | false | +| Flag | Shorthand | Env | Type | Default | +|------|:---------:|------|:----:|:-------:| +| --help | -h | HELP | bool | false | `, sb.String()) }, @@ -103,9 +103,9 @@ description: A complete list of all Foo flags, with descriptions, defaults, and Show help information and exit -| Flag | Env | Type | Default | -|------|------|:----:|:-------:| -| --help | HELP | bool | false | +| Flag | Shorthand | Env | Type | Default | +|------|:---------:|------|:----:|:-------:| +| --help | -h | HELP | bool | false | `, sb.String()) }, @@ -164,9 +164,9 @@ Some long description here Show help information and exit -| Flag | Env | Type | Default | -|------|------|:----:|:-------:| -| --help | HELP | bool | false | +| Flag | Shorthand | Env | Type | Default | +|------|:---------:|------|:----:|:-------:| +| --help | -h | HELP | bool | false | `, "```bash tab=CLI\n--foo bar\n```\n```bash tab=Env\nFOO=bar\n```\n```bash tab=File\nfoo: bar\n```"), sb.String()) }, diff --git a/pkg/cli/help.go b/pkg/cli/help.go index 9dcbedc4a..441e03f34 100644 --- a/pkg/cli/help.go +++ b/pkg/cli/help.go @@ -26,7 +26,13 @@ func (c *Command) printHelp() { _, _ = fmt.Fprintf(w, "\nFlags:") maxNameLen, hasShort := flagsInfo(flags) + done := map[string]bool{} _ = flags.Visit(func(flag *Flag) error { + // skip aliases and short + if done[flag.Name] { + return nil + } + done[flag.Name] = true _, _ = fmt.Fprintln(w) if hasShort { diff --git a/pkg/cli/help_test.go b/pkg/cli/help_test.go index 0d7321250..8c101d11c 100644 --- a/pkg/cli/help_test.go +++ b/pkg/cli/help_test.go @@ -21,7 +21,7 @@ func TestHelp(t *testing.T) { return c }(), test: func(t *testing.T, out string) { - require.Equal(t, "\nFlags:\n --help Show help information and exit\n", out) + require.Equal(t, "\nFlags:\n -h, --help Show help information and exit\n", out) }, }, { @@ -31,7 +31,7 @@ func TestHelp(t *testing.T) { return c }(), test: func(t *testing.T, out string) { - require.Equal(t, "\n\nLong Description\n\nFlags:\n --help Show help information and exit\n", out) + require.Equal(t, "\n\nLong Description\n\nFlags:\n -h, --help Show help information and exit\n", out) }, }, } From 1f85e01a07a0cb25d9835a7cbcfdaa1f4f9bf28f Mon Sep 17 00:00:00 2001 From: maesi Date: Thu, 5 Feb 2026 19:17:55 +0100 Subject: [PATCH 03/75] fix: add GIT to image for testing GIT provider --- images/alpha.Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/images/alpha.Dockerfile b/images/alpha.Dockerfile index 73ffde179..b2ce6bccd 100644 --- a/images/alpha.Dockerfile +++ b/images/alpha.Dockerfile @@ -17,6 +17,9 @@ ARG VERSION=dev ARG BUILD_TIME=dev +# Install git for GIT tests +RUN apk add --no-cache git + COPY . /go/src/github.com/mokapi WORKDIR /go/src/github.com/mokapi From acb03fe0391016bf0e9eb8ece92d4c17780c6c46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 07:43:52 +0000 Subject: [PATCH 04/75] Bump github.com/evanw/esbuild from 0.27.2 to 0.27.3 Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.27.2 to 0.27.3. - [Release notes](https://github.com/evanw/esbuild/releases) - [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md) - [Commits](https://github.com/evanw/esbuild/compare/v0.27.2...v0.27.3) --- updated-dependencies: - dependency-name: github.com/evanw/esbuild dependency-version: 0.27.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index e57cad3d8..a75ab4d4d 100644 --- a/go.mod +++ b/go.mod @@ -9,10 +9,11 @@ require ( github.com/bradleyfalzon/ghinstallation/v2 v2.17.0 github.com/brianvoe/gofakeit/v6 v6.28.0 github.com/dop251/goja v0.0.0-20250309171923-bcd7cc6bf64c - github.com/evanw/esbuild v0.27.2 + github.com/evanw/esbuild v0.27.3 github.com/fsnotify/fsnotify v1.9.0 github.com/go-co-op/gocron v1.37.0 github.com/go-git/go-git/v5 v5.16.4 + github.com/golang-jwt/jwt/v4 v4.5.2 github.com/google/uuid v1.6.0 github.com/jinzhu/inflection v1.0.0 github.com/pkg/errors v0.9.1 @@ -58,7 +59,6 @@ require ( github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect - github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-github/v75 v75.0.0 // indirect diff --git a/go.sum b/go.sum index 7ecb3f6ff..1d553f2a9 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/evanw/esbuild v0.27.2 h1:3xBEws9y/JosfewXMM2qIyHAi+xRo8hVx475hVkJfNg= -github.com/evanw/esbuild v0.27.2/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= +github.com/evanw/esbuild v0.27.3 h1:dH/to9tBKybig6hl25hg4SKIWP7U8COdJKbGEwnUkmU= +github.com/evanw/esbuild v0.27.3/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= From 0dadf11ce8f3e73216b0516a4ee534bdd94f217e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 07:43:54 +0000 Subject: [PATCH 05/75] Bump vue-router from 5.0.0 to 5.0.2 in /webui Bumps [vue-router](https://github.com/vuejs/router) from 5.0.0 to 5.0.2. - [Release notes](https://github.com/vuejs/router/releases) - [Commits](https://github.com/vuejs/router/compare/v5.0.0...v5.0.2) --- updated-dependencies: - dependency-name: vue-router dependency-version: 5.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- webui/package-lock.json | 21 ++++++++++----------- webui/package.json | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/webui/package-lock.json b/webui/package-lock.json index e0d2b55ee..a7b35f45c 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -30,7 +30,7 @@ "ncp": "^2.0.0", "nodemailer": "^7.0.13", "vue": "^3.5.27", - "vue-router": "^5.0.0", + "vue-router": "^5.0.2", "vue3-ace-editor": "^2.2.4", "vue3-highlightjs": "^1.0.5", "vue3-markdown-it": "^1.0.10", @@ -7147,18 +7147,17 @@ } }, "node_modules/unplugin": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", - "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.0.0.tgz", + "integrity": "sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==", "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", - "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" }, "engines": { - "node": ">=18.12.0" + "node": "^20.19.0 || >=22.12.0" } }, "node_modules/unplugin-utils": { @@ -7425,9 +7424,9 @@ } }, "node_modules/vue-router": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.0.0.tgz", - "integrity": "sha512-xWHlps4o1ScODWqvyapl0v1uGy0g7ozmsTSO/dguyGb/9RL6oSU2HfN/8oMXnoFOH1BuTaAkbiOz4OWdkfjcZg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.0.2.tgz", + "integrity": "sha512-YFhwaE5c5JcJpNB1arpkl4/GnO32wiUWRB+OEj1T0DlDxEZoOfbltl2xEwktNU/9o1sGcGburIXSpbLpPFe/6w==", "license": "MIT", "dependencies": { "@babel/generator": "^7.28.6", @@ -7444,7 +7443,7 @@ "picomatch": "^4.0.3", "scule": "^1.3.0", "tinyglobby": "^0.2.15", - "unplugin": "^2.3.11", + "unplugin": "^3.0.0", "unplugin-utils": "^0.3.1", "yaml": "^2.8.2" }, @@ -7452,7 +7451,7 @@ "url": "https://github.com/sponsors/posva" }, "peerDependencies": { - "@pinia/colada": "^0.18.1", + "@pinia/colada": ">=0.21.2", "@vue/compiler-sfc": "^3.5.17", "pinia": "^3.0.4", "vue": "^3.5.0" diff --git a/webui/package.json b/webui/package.json index 9b188f55c..a39481e7d 100644 --- a/webui/package.json +++ b/webui/package.json @@ -39,7 +39,7 @@ "ncp": "^2.0.0", "nodemailer": "^7.0.13", "vue": "^3.5.27", - "vue-router": "^5.0.0", + "vue-router": "^5.0.2", "vue3-ace-editor": "^2.2.4", "vue3-highlightjs": "^1.0.5", "vue3-markdown-it": "^1.0.10", From b059bf8052850474de717610a9e9174ea2b19423 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 07:51:41 +0000 Subject: [PATCH 06/75] Bump @vitejs/plugin-vue from 6.0.3 to 6.0.4 in /webui Bumps [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/HEAD/packages/plugin-vue) from 6.0.3 to 6.0.4. - [Release notes](https://github.com/vitejs/vite-plugin-vue/releases) - [Changelog](https://github.com/vitejs/vite-plugin-vue/blob/main/packages/plugin-vue/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite-plugin-vue/commits/plugin-vue@6.0.4/packages/plugin-vue) --- updated-dependencies: - dependency-name: "@vitejs/plugin-vue" dependency-version: 6.0.4 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- webui/package-lock.json | 16 ++++++++-------- webui/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/webui/package-lock.json b/webui/package-lock.json index a7b35f45c..1564eb416 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -42,7 +42,7 @@ "@rushstack/eslint-patch": "^1.15.0", "@types/js-yaml": "^4.0.9", "@types/node": "^25.1.0", - "@vitejs/plugin-vue": "^6.0.3", + "@vitejs/plugin-vue": "^6.0.4", "@vue/eslint-config-prettier": "^10.2.0", "@vue/eslint-config-typescript": "^14.6.0", "@vue/tsconfig": "^0.8.1", @@ -911,9 +911,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.53", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", - "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", + "version": "1.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz", + "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==", "dev": true, "license": "MIT" }, @@ -1632,13 +1632,13 @@ } }, "node_modules/@vitejs/plugin-vue": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.3.tgz", - "integrity": "sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.4.tgz", + "integrity": "sha512-uM5iXipgYIn13UUQCZNdWkYk+sysBeA97d5mHsAoAt1u/wpN3+zxOmsVJWosuzX+IMGRzeYUNytztrYznboIkQ==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "1.0.0-beta.53" + "@rolldown/pluginutils": "1.0.0-rc.2" }, "engines": { "node": "^20.19.0 || >=22.12.0" diff --git a/webui/package.json b/webui/package.json index a39481e7d..b16e2a69c 100644 --- a/webui/package.json +++ b/webui/package.json @@ -51,7 +51,7 @@ "@rushstack/eslint-patch": "^1.15.0", "@types/js-yaml": "^4.0.9", "@types/node": "^25.1.0", - "@vitejs/plugin-vue": "^6.0.3", + "@vitejs/plugin-vue": "^6.0.4", "@vue/eslint-config-prettier": "^10.2.0", "@vue/eslint-config-typescript": "^14.6.0", "@vue/tsconfig": "^0.8.1", From 8bcc58ab45cbc432eee01dae57b248f62bd71211 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 07:59:16 +0000 Subject: [PATCH 07/75] Bump @types/node from 25.1.0 to 25.2.1 in /webui Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.1.0 to 25.2.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 25.2.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- webui/package-lock.json | 8 ++++---- webui/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webui/package-lock.json b/webui/package-lock.json index 1564eb416..6a312db3b 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -41,7 +41,7 @@ "@playwright/test": "^1.57.0", "@rushstack/eslint-patch": "^1.15.0", "@types/js-yaml": "^4.0.9", - "@types/node": "^25.1.0", + "@types/node": "^25.2.1", "@vitejs/plugin-vue": "^6.0.4", "@vue/eslint-config-prettier": "^10.2.0", "@vue/eslint-config-typescript": "^14.6.0", @@ -1365,9 +1365,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.1.0.tgz", - "integrity": "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.1.tgz", + "integrity": "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg==", "license": "MIT", "dependencies": { "undici-types": "~7.16.0" diff --git a/webui/package.json b/webui/package.json index b16e2a69c..d77535164 100644 --- a/webui/package.json +++ b/webui/package.json @@ -50,7 +50,7 @@ "@playwright/test": "^1.57.0", "@rushstack/eslint-patch": "^1.15.0", "@types/js-yaml": "^4.0.9", - "@types/node": "^25.1.0", + "@types/node": "^25.2.1", "@vitejs/plugin-vue": "^6.0.4", "@vue/eslint-config-prettier": "^10.2.0", "@vue/eslint-config-typescript": "^14.6.0", From e149f3e69882e83059f3fa9359adad70e930c8f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 08:07:36 +0000 Subject: [PATCH 08/75] Bump ldapts from 8.1.3 to 8.1.6 in /webui Bumps [ldapts](https://github.com/ldapts/ldapts) from 8.1.3 to 8.1.6. - [Release notes](https://github.com/ldapts/ldapts/releases) - [Changelog](https://github.com/ldapts/ldapts/blob/main/CHANGELOG.md) - [Commits](https://github.com/ldapts/ldapts/compare/v8.1.3...v8.1.6) --- updated-dependencies: - dependency-name: ldapts dependency-version: 8.1.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- webui/package-lock.json | 46 ++++++----------------------------------- webui/package.json | 2 +- 2 files changed, 7 insertions(+), 41 deletions(-) diff --git a/webui/package-lock.json b/webui/package-lock.json index 6a312db3b..6168f93ec 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -25,7 +25,7 @@ "http-status-codes": "^2.3.0", "js-yaml": "^4.1.1", "kafkajs": "^2.2.4", - "ldapts": "^8.1.3", + "ldapts": "^8.1.6", "mime-types": "^3.0.2", "ncp": "^2.0.0", "nodemailer": "^7.0.13", @@ -4817,13 +4817,12 @@ } }, "node_modules/ldapts": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/ldapts/-/ldapts-8.1.3.tgz", - "integrity": "sha512-kEU3GDh48ZymnyLGsFprai2v4r7Gyxe6niBlUUw3xnOGpq5O+XODmXJ8gBwbPIg35qt5cnYVC80NNSdAkb2dJg==", + "version": "8.1.6", + "resolved": "https://registry.npmjs.org/ldapts/-/ldapts-8.1.6.tgz", + "integrity": "sha512-sofxzGEPRBvubSrdmly0mmUwjXHPfTbO51KLAUzuO4sHWwy+r0G6FwaLWWDwTPRpjJFkMdLId5BeRUHksUH4yA==", "license": "MIT", "dependencies": { - "strict-event-emitter-types": "2.0.0", - "whatwg-url": "15.1.0" + "strict-event-emitter-types": "2.0.0" }, "engines": { "node": ">=20" @@ -5976,6 +5975,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -6920,18 +6920,6 @@ "node": ">=0.6" } }, - "node_modules/tr46": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", - "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=20" - } - }, "node_modules/ts-api-utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", @@ -7543,15 +7531,6 @@ "markdown-it-toc-done-right": "^4.2.0" } }, - "node_modules/webidl-conversions": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", - "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=20" - } - }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", @@ -7567,19 +7546,6 @@ "node": ">=20" } }, - "node_modules/whatwg-url": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz", - "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", - "license": "MIT", - "dependencies": { - "tr46": "^6.0.0", - "webidl-conversions": "^8.0.0" - }, - "engines": { - "node": ">=20" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/webui/package.json b/webui/package.json index d77535164..58606c63c 100644 --- a/webui/package.json +++ b/webui/package.json @@ -34,7 +34,7 @@ "http-status-codes": "^2.3.0", "js-yaml": "^4.1.1", "kafkajs": "^2.2.4", - "ldapts": "^8.1.3", + "ldapts": "^8.1.6", "mime-types": "^3.0.2", "ncp": "^2.0.0", "nodemailer": "^7.0.13", From b527c58e408e2f4a8e051fa51341e9ab5bf362ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 08:15:46 +0000 Subject: [PATCH 09/75] Bump nodemailer from 7.0.13 to 8.0.0 in /webui Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 7.0.13 to 8.0.0. - [Release notes](https://github.com/nodemailer/nodemailer/releases) - [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodemailer/nodemailer/compare/v7.0.13...v8.0.0) --- updated-dependencies: - dependency-name: nodemailer dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- webui/package-lock.json | 8 ++++---- webui/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webui/package-lock.json b/webui/package-lock.json index 6168f93ec..d3414d105 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -28,7 +28,7 @@ "ldapts": "^8.1.6", "mime-types": "^3.0.2", "ncp": "^2.0.0", - "nodemailer": "^7.0.13", + "nodemailer": "^8.0.0", "vue": "^3.5.27", "vue-router": "^5.0.2", "vue3-ace-editor": "^2.2.4", @@ -5269,9 +5269,9 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.13.tgz", - "integrity": "sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.0.tgz", + "integrity": "sha512-xvVJf/f0bzmNpnRIbhCp/IKxaHgJ6QynvUbLXzzMRPG3LDQr5oXkYuw4uDFyFYs8cge8agwwrJAXZsd4hhMquw==", "license": "MIT-0", "engines": { "node": ">=6.0.0" diff --git a/webui/package.json b/webui/package.json index 58606c63c..68b617677 100644 --- a/webui/package.json +++ b/webui/package.json @@ -37,7 +37,7 @@ "ldapts": "^8.1.6", "mime-types": "^3.0.2", "ncp": "^2.0.0", - "nodemailer": "^7.0.13", + "nodemailer": "^8.0.0", "vue": "^3.5.27", "vue-router": "^5.0.2", "vue3-ace-editor": "^2.2.4", From da8f20c32d90e146bf55cc304fc6cb7eb6572b10 Mon Sep 17 00:00:00 2001 From: maesi Date: Fri, 6 Feb 2026 23:31:58 +0100 Subject: [PATCH 10/75] update documentation Welcome and Installation --- docs/guides/get-started/installation.md | 69 ++++++++++++++++++------- docs/guides/get-started/welcome.md | 58 ++++++++++----------- webui/package-lock.json | 25 +++++++-- webui/package.json | 2 + webui/src/assets/tabs.css | 42 ++++++++++++++- webui/src/assets/vars.css | 8 ++- webui/src/composables/markdown.ts | 4 +- 7 files changed, 148 insertions(+), 60 deletions(-) diff --git a/docs/guides/get-started/installation.md b/docs/guides/get-started/installation.md index 8bd6b15d4..7b6311620 100644 --- a/docs/guides/get-started/installation.md +++ b/docs/guides/get-started/installation.md @@ -4,18 +4,46 @@ description: Learn how to install Mokapi effortlessly across Windows, macOS, and --- # Install Mokapi -Mokapi is an open-source tool designed to simplify API mocking and schema validation, enabling developers to prototype, test, and demonstrate APIs with realistic data and scenarios. This guide provides step-by-step instructions to install Mokapi on various platforms, ensuring a smooth setup process. +Mokapi is an open-source tool designed to simplify API mocking and schema validation. +It enables developers to prototype, test, and demonstrate APIs with realistic data and +scenarios. This guide provides straightforward instructions to install Mokapi on various +platforms. -## Docker +## Installation Options -Visit [DockerHub](https://hub.docker.com/r/mokapi/mokapi/tags) for a list of all available images. -You can also use a custom base Docker image as shown in [these examples](/docs/resources/examples/mokapi-with-custom-base-image.md). +Mokapi can be installed via direct download or through package managers on supported platforms. +Choose your preferred method below: +::: tabs + +@tab "macOS" + +### Homebrew + +```bash +brew tap marle3003/tap +brew install mokapi ``` -docker pull mokapi/mokapi + +### Direct Download + +Download the latest macOS version from [GitHub](https://github.com/marle3003/mokapi/releases) + +@tab "Windows" + +### Chocolatey + +```Powershell +choco install mokapi ``` -## Linux +### Direct Download + +Download the latest Windows version from [GitHub](https://github.com/marle3003/mokapi/releases) + +@tab "Linux" + +### Direct Download Download file appropriate for your Linux distribution and ARCH from the [release page](https://github.com/marle3003/mokapi/releases), then install with @@ -27,32 +55,33 @@ dpkg -i mokapi_{version}_linux_{arch}.deb rpm -i mokapi_{version}_linux_{arch}.rpm ``` -## MacOS +@tab "Docker" -Using [Homebrew](https://brew.sh/): +To get started with Mokapi using Docker, visit [DockerHub](https://hub.docker.com/r/mokapi/mokapi/tags) for a list of available images. +You can also use a custom base Docker image as demonstrated in [these examples](/docs/resources/examples/mokapi-with-custom-base-image.md). ``` -brew tap marle3003/tap -brew install mokapi +docker pull mokapi/mokapi ``` -## Windows - -Install Mokapi by [Chocolatey package manager](https://chocolatey.org/) with: - -```Powershell -choco install mokapi -``` +@tab "NPM" -## NodeJS npm package +If you prefer to install Mokapi as a Node.js package, use the following command: ```bash npm install go-mokapi ``` -## Download binary +::: + +### Mokapi Scripts Type Definitions -You can download Mokapi's binary file from [GitHub Releases page](https://github.com/marle3003/mokapi/releases) +Mokapi allows you to write **custom scripts** to handle API events or modify responses. +For full type safety and autocompletion in TypeScript, you can install the [`@types/mokapi`](https://www.npmjs.com/package/@types/mokapi`) package: + +```bash +npm install --save-dev @types/mokapi +``` ## Next steps diff --git a/docs/guides/get-started/welcome.md b/docs/guides/get-started/welcome.md index 6a187bdcb..cec0d0f02 100644 --- a/docs/guides/get-started/welcome.md +++ b/docs/guides/get-started/welcome.md @@ -25,49 +25,47 @@ cards: # Mocking APIs with Mokapi -Welcome to Mokapi! Mokapi is a powerful, flexible platform for building, testing, and monitoring API-driven applications. This guide will help you quickly understand how to get started with Mokapi and introduce you to its key features. +**Welcome to Mokapi!** -> *Mokapi is your always-on API contract guardian — lightweight, transparent, and spec-driven.* +Mokapi is your go-to platform for mocking APIs, making it easy to build, +test, and monitor API-driven applications without the hassle. -## Build Better Software with Mokapi - -Modern applications rely on dozens of APIs—many of them outside your control. That creates friction: slow testing, fragile pipelines, and blocked teams. - -Mokapi was created to remove those barriers. -It empowers developers to mock, simulate, and explore APIs with ease, so you can: +> *Think of Mokapi as your ever-reliable API contract guardian—lightweight, transparent, and specification-driven.* -- **Develop without waiting** for external systems to be ready. -- **Test with confidence** against realistic API behavior. -- **Automate your workflows with CI/CD pipelines that stay reliable.** -- **Stay future-proof** by safely integrating with tools like Dependabot or Renovate. - -Mokapi is more than just a mocking tool—it’s a way to unlock faster feedback, smoother collaboration, and higher quality software. +## Build Better Software with Mokapi -> Because building and testing great software shouldn’t depend on systems you can’t control. +In today's world, modern applications rely on multiple external APIs. +When these APIs are slow, unreliable, or unavailable, they can impede +your development process. Mokapi eliminates these obstacles, enabling +you to: -## Why Choose Mokapi? +- **Develop Faster:** Eliminate waiting times by working independently of external systems. +- **Test with Confidence:** Simulate realistic API behaviors, including edge cases. +- **Automate Your Pipelines:** Enhance CI/CD reliability with consistent mock responses. +- **Ensure Compliance:** Seamlessly integrate tools like Dependabot or Renovate for future-proof dependencies. -Mokapi offers several key benefits: +## Key Features --

Easy API Mocking:
Quickly mock REST, SOAP, or event-driven APIs with minimal setup. Mokapi automatically generates mock servers based on your OpenAPI or AsyncAPI specifications, allowing you to simulate and test APIs in seconds.

--

No-Code Setup:
Mokapi allows you to mock and test APIs with no coding effort.

--

Real-Time Monitoring and Analytics:
With Mokapi’s interactive dashboard, you can monitor and analyze requests and responses in real-time. Get detailed insights into API behavior, errors, and status codes to help streamline debugging and testing.

--

Flexible Test Data Generation:
Mokapi’s random data generator allows you to simulate realistic test data. You can customize data patterns and even create dynamic data based on your APIs needs, making it ideal for testing edge cases and complex workflows.

--

Seamless CI/CD Integration:
Mokapi easily integrates into your continuous integration and deployment (CI/CD) pipeline. Run automated tests and validations for your APIs without any manual intervention, improving efficiency and reducing human error.

--

Free and Open-Source:
Mokapi is an open-source project, so you can start using it without any licensing fees. It’s free to use and offers transparency, flexibility, and the ability to contribute to the project’s development.

--

Rapid API Prototyping:
Use Mokapi for fast prototyping of new APIs and services. Mock APIs before they’re built or deployed to ensure that your development teams can begin their work immediately, even in the absence of a fully functional backend.

--

Collaborative API Development:
Mokapi enables teams to collaborate efficiently by providing an easily accessible testing environment. Developers, QA testers, and product teams can work together on mocking and validating APIs in real time.

+-

Spec-Driven Mocking:
Quickly create OpenAPI or AsyncAPI mock servers for REST, SOAP, and event-driven architectures with minimal setup.

+-

No-Code Configuration:
Jump right in—no complex coding required!

+-

Live Monitoring:
Use the [Mokapi Dashboard](docs/guides/get-started/dashboard) to track requests and responses in real-time, simplifying your debugging process.

+-

Dynamic Test Data:
Utilize the built-in random data generator to create realistic payloads tailored to your API needs.

+-

Local & Secure:
An offline-first tool, Mokapi keeps your data on your machine—no accounts, no cloud syncs, and no identity tracking.

+-

Open Source:
Free and transparent, explore Mokapi's source code on [GitHub](https://github.com/marle3003/mokapi).

## Data Privacy and Security -Mokapi is designed as a local, offline-first tool. Your data stays on your machine and under your control at all times. - -Mokapi does not require you to create an account or log in. There is no user authentication, tracking, or identity management involved. +Mokapi prioritizes your privacy: -Mokapi does not connect to any cloud services and does not sync your data anywhere. API specifications, mock configurations, requests, responses, and generated data remain local unless you explicitly choose to share them yourself. +- **Local Control:** Your data remains on your machine at all times. +- **No Account Needed:** Enjoy a hassle-free experience without the need for user accounts or authentication. +- **No Cloud Connectivity:** Mokapi operates entirely offline, ensuring your data—including API specifications, mock configurations, requests, and responses—stays private. ## Explore how you can mock your APIs with Mokapi -Whether you are mocking APIs for local testing or validating event-driven systems, Mokapi makes the process seamless and efficient. With Mokapi’s powerful tools and integrations, you can accelerate your development cycle, reduce errors, and improve the quality of your APIs. +Whether you're mocking APIs for local testing or validating event-driven +systems, Mokapi streamlines the process. With its powerful tools and +integrations, you can accelerate your development cycle, minimize errors, +and enhance the quality of your APIs. {{ card-grid key="cards" }} diff --git a/webui/package-lock.json b/webui/package-lock.json index e0d2b55ee..1f0c9a31e 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -26,6 +26,7 @@ "js-yaml": "^4.1.1", "kafkajs": "^2.2.4", "ldapts": "^8.1.3", + "markdown-it-container": "^4.0.0", "mime-types": "^3.0.2", "ncp": "^2.0.0", "nodemailer": "^7.0.13", @@ -41,6 +42,7 @@ "@playwright/test": "^1.57.0", "@rushstack/eslint-patch": "^1.15.0", "@types/js-yaml": "^4.0.9", + "@types/markdown-it-container": "^4.0.0", "@types/node": "^25.1.0", "@vitejs/plugin-vue": "^6.0.3", "@vue/eslint-config-prettier": "^10.2.0", @@ -1337,26 +1339,33 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/markdown-it": { "version": "14.1.2", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", "license": "MIT", - "peer": true, "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, + "node_modules/@types/markdown-it-container": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/markdown-it-container/-/markdown-it-container-4.0.0.tgz", + "integrity": "sha512-GmD8OECLfzPHv8VyvFRzslqdwXoDBJ2H40fxXFjrarbqvJZSB/BJKZXN5e3k7Mx7GQanSNzTYhzeS3H9o0gAOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/markdown-it": ">=14" + } + }, "node_modules/@types/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/mokapi": { "version": "0.29.1", @@ -4970,6 +4979,12 @@ "markdown-it": "*" } }, + "node_modules/markdown-it-container": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-container/-/markdown-it-container-4.0.0.tgz", + "integrity": "sha512-HaNccxUH0l7BNGYbFbjmGpf5aLHAMTinqRZQAEQbMr2cdD3z91Q6kIo1oUn1CQndkT03jat6ckrdRYuwwqLlQw==", + "license": "MIT" + }, "node_modules/markdown-it-deflist": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/markdown-it-deflist/-/markdown-it-deflist-2.1.0.tgz", diff --git a/webui/package.json b/webui/package.json index 9b188f55c..82046f2c4 100644 --- a/webui/package.json +++ b/webui/package.json @@ -35,6 +35,7 @@ "js-yaml": "^4.1.1", "kafkajs": "^2.2.4", "ldapts": "^8.1.3", + "markdown-it-container": "^4.0.0", "mime-types": "^3.0.2", "ncp": "^2.0.0", "nodemailer": "^7.0.13", @@ -50,6 +51,7 @@ "@playwright/test": "^1.57.0", "@rushstack/eslint-patch": "^1.15.0", "@types/js-yaml": "^4.0.9", + "@types/markdown-it-container": "^4.0.0", "@types/node": "^25.1.0", "@vitejs/plugin-vue": "^6.0.3", "@vue/eslint-config-prettier": "^10.2.0", diff --git a/webui/src/assets/tabs.css b/webui/src/assets/tabs.css index 0cdf5b51e..e2a914c38 100644 --- a/webui/src/assets/tabs.css +++ b/webui/src/assets/tabs.css @@ -69,7 +69,45 @@ .tab-pane { padding: 0.8rem; padding-top: 1.6rem; - } +} + +.tabs { + border: none; + padding: 16px 0; + + .nav-tabs { + border: none; + } + + > .nav-tabs button { + border: none; + background-color: transparent; + padding: 0 8px 4px; + } + + > .nav-tabs button[role="tab"].active, > .nav-tabs button[role="tab"]:hover { + border-color: var(--code-tabs-border-color-active); + border-bottom-width: 3px; + border-bottom-style: solid; + margin-bottom: -3px; + } + + > .nav-tabs .tabs-border { + width: 100%; + height: 2px; + background-color: var(--tabs-border-color); + margin: 0 auto; + } + + .tab-content { + margin-top: 20px; + } + + .tab-pane { + padding: 0; + } +} + .code { background-color: var(--code-background); color: var(--code-color); @@ -123,6 +161,6 @@ .tabs-border { width: 100%; height: 3px; - background-color: var(--code-tab-border-color); + background-color: var(--code-tabs-border-color); margin: 0 auto; } \ No newline at end of file diff --git a/webui/src/assets/vars.css b/webui/src/assets/vars.css index cc39d75f6..55b16509f 100644 --- a/webui/src/assets/vars.css +++ b/webui/src/assets/vars.css @@ -98,12 +98,14 @@ --badge-background: #eabaabff; + --tabs-border-color: rgba(255, 255, 255, 0.1); + --code-background: #0d1117; --code-color: #fff; --code-tabs-color: #d3d4d5; --code-tabs-color-active: #eabaabff; --code-tabs-border-color-active: #eabaabff; - --code-tab-border-color: #3c424b; + --code-tabs-border-color: #3c424b; --code-control-color-active: #eabaabff; --blockquote-border-color: #eabaabff; @@ -183,12 +185,14 @@ --badge-background: rgb(8, 109, 215); + --tabs-border-color: rgba(0, 0, 0, 0.1); + --code-background: #0d1117; --code-color: rgb(255, 255, 255);; --code-tabs-color: rgb(255,255,255); --code-tabs-color-active: rgb(255, 255, 255); --code-tabs-border-color-active: rgb(8, 109, 215); - --code-tab-border-color: #3c424b; + --code-tabs-border-color: #3c424b; --code-control-color-active: rgb(8, 109, 215); --blockquote-border-color: rgb(8, 109, 215); diff --git a/webui/src/composables/markdown.ts b/webui/src/composables/markdown.ts index 71e5b49e8..e5fb3265b 100644 --- a/webui/src/composables/markdown.ts +++ b/webui/src/composables/markdown.ts @@ -7,6 +7,7 @@ import { MarkdownItCard } from '@/composables/markdown-card'; import { MarkdownItCarousel } from './markdown-carousel'; import yaml from 'js-yaml' import { MarkdownItBlockquote } from './markdown-blockquote'; +import { MarkdownItTabContent } from './markdown-tab-content'; const images = import.meta.glob('/src/assets/docs/**/*.png', {as: 'url', eager: true}) const metadataRegex = /^---([\s\S]*?)---/; @@ -26,6 +27,7 @@ export function useMarkdown(content: string | undefined): {content: string | und .use(MarkdownItHighlightjs) .use(MarkdownItBlockquote) .use(MarkdownItTabs) + .use(MarkdownItTabContent) .use(MarkdownItBox) .use(MarkdownItLinks) .use(MarkdownItCarousel(metadata)) @@ -36,7 +38,7 @@ export function useMarkdown(content: string | undefined): {content: string | und return {content, metadata} } catch (e) { - console.error('invalid markdown: '+content) + console.error('invalid markdown: '+e) return { content: '' } } } From 67010fa4ef3eb11943f54ddb92290f575be98419 Mon Sep 17 00:00:00 2001 From: maesi Date: Fri, 6 Feb 2026 23:56:07 +0100 Subject: [PATCH 11/75] update package-lock.json --- webui/package-lock.json | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/webui/package-lock.json b/webui/package-lock.json index d3414d105..4cd9b0e02 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -26,6 +26,7 @@ "js-yaml": "^4.1.1", "kafkajs": "^2.2.4", "ldapts": "^8.1.6", + "markdown-it-container": "^4.0.0", "mime-types": "^3.0.2", "ncp": "^2.0.0", "nodemailer": "^8.0.0", @@ -41,6 +42,7 @@ "@playwright/test": "^1.57.0", "@rushstack/eslint-patch": "^1.15.0", "@types/js-yaml": "^4.0.9", + "@types/markdown-it-container": "^4.0.0", "@types/node": "^25.2.1", "@vitejs/plugin-vue": "^6.0.4", "@vue/eslint-config-prettier": "^10.2.0", @@ -1337,26 +1339,33 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/markdown-it": { "version": "14.1.2", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", "license": "MIT", - "peer": true, "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, + "node_modules/@types/markdown-it-container": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/markdown-it-container/-/markdown-it-container-4.0.0.tgz", + "integrity": "sha512-GmD8OECLfzPHv8VyvFRzslqdwXoDBJ2H40fxXFjrarbqvJZSB/BJKZXN5e3k7Mx7GQanSNzTYhzeS3H9o0gAOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/markdown-it": ">=14" + } + }, "node_modules/@types/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/mokapi": { "version": "0.29.1", @@ -4969,6 +4978,12 @@ "markdown-it": "*" } }, + "node_modules/markdown-it-container": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-container/-/markdown-it-container-4.0.0.tgz", + "integrity": "sha512-HaNccxUH0l7BNGYbFbjmGpf5aLHAMTinqRZQAEQbMr2cdD3z91Q6kIo1oUn1CQndkT03jat6ckrdRYuwwqLlQw==", + "license": "MIT" + }, "node_modules/markdown-it-deflist": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/markdown-it-deflist/-/markdown-it-deflist-2.1.0.tgz", From 90e81f3d0187fcc1d12ec321a98ede914d7ea5d6 Mon Sep 17 00:00:00 2001 From: maesi Date: Fri, 6 Feb 2026 23:58:49 +0100 Subject: [PATCH 12/75] add missing file --- webui/src/composables/markdown-tab-content.ts | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 webui/src/composables/markdown-tab-content.ts diff --git a/webui/src/composables/markdown-tab-content.ts b/webui/src/composables/markdown-tab-content.ts new file mode 100644 index 000000000..c273c39e0 --- /dev/null +++ b/webui/src/composables/markdown-tab-content.ts @@ -0,0 +1,134 @@ +import type MarkdownIt from "markdown-it" +import type { Options } from "markdown-it" +import container from "markdown-it-container" +import type { Renderer } from "markdown-it/dist/index.cjs.js" +import Token from 'markdown-it/lib/token' + +export function MarkdownItTabContent(md: MarkdownIt, _opts: Options) { + + md.core.ruler.push('test', function (state) { + let hide = false + let inTab = false + + for (let i = 0; i < state.tokens.length; i++) { + const token = state.tokens[i]! + + switch (token.type) { + case 'container_tabs_open': + hide = true + continue + case 'container_tabs_close': + hide = false + inTab = false + continue + case 'inline': + if (token.content.startsWith('@tab')) { + const match = token.content.trim().match(/^@tab "(.*)"$/); + if (!match || !match[1]) { + console.error('invalid tab format: ' + token.content) + return + } + const label = match[1]; + const safe = label.replace(/[^\w-]/g, "-"); + const tabIndex = tabs.length + const tabId = `tab-${tabIndex}-${safe}` + const panelId = `tabPanel-${tabIndex}-${safe}` + + const button = ` + ` + + tabs.push({ + index: tabIndex, + panelId: panelId, + button: button, + panel: '', + tokens: [] + }) + inTab = true + hideToken(token); + i++; // skip closing paragraph + continue + } + } + + if (hide) { + hideToken(token) + if (inTab) { + tabs[tabs.length - 1]!.tokens.push(token) + } + } + } + }) + + function hideToken(token: Token) { + token.hidden = true + if (token.children) { + for (const child of token.children) { + hideToken(child) + } + } + } + + interface TabsState { + index: number + panelId: string + button: string + panel: string + tokens: Token[] + } + + const tabs: TabsState[] = [] + + md.use(container, 'tabs', { + validate: (params: any) => params.trim() === 'tabs', + render: (tokens: Token[], idx: number, options: Options, env: any, slf: Renderer) => { + const token = tokens[idx]; + if (token?.nesting === 1) { + return '' + } else { + return ` +
+ +
+ ${tabs.map(x => { + const tokens = x.tokens.map(t => clone(t)) + const content = md.renderer.render(tokens, options, env) + return ` +
+ ${content} +
+ `; + }).join('')} +
+
` + } + } + }); + + function clone(token: Token): Token { + const t = Object.create(Object.getPrototypeOf(token)) + Object.assign(t, token) + + t.hidden = false + if(token.attrs) { + t.attrs = token.attrs.map((a: any) => [...a]) + } + + if (token.children) { + t.children = token.children.map(clone) + } + return t + } +} From 6f20f0bfbf0a7f638754efe9801e52c3eb204bc5 Mon Sep 17 00:00:00 2001 From: maesi Date: Sat, 7 Feb 2026 12:38:56 +0100 Subject: [PATCH 13/75] switch to grid system --- webui/src/assets/home.css | 24 +------------ webui/src/assets/main.css | 18 +++++++--- webui/src/components/Footer.vue | 29 ++++++++++++++- webui/src/views/DocsView.vue | 63 ++++++++++++++++++--------------- 4 files changed, 76 insertions(+), 58 deletions(-) diff --git a/webui/src/assets/home.css b/webui/src/assets/home.css index 37993b962..b14123efb 100644 --- a/webui/src/assets/home.css +++ b/webui/src/assets/home.css @@ -184,29 +184,7 @@ section.feature button { margin-top: 1rem; margin-bottom: 1rem; } -footer { - padding-top: 3rem; - padding-bottom: 2rem; - background-color: var(--footer-background); - opacity: 0.8; - font-size: 0.9rem; -} -footer h3 { - font-size: 0.9rem; - margin-bottom: 1.2rem; - font-weight: 600; - text-transform: uppercase; -} -footer ul { - list-style-type: none; - padding-left: 0; -} -footer ul li { - margin-bottom: 0.5rem; -} -footer a { - text-decoration: none; -} + .home code { font-size: 14px; font-family: Menlo,Monaco,Consolas,"Courier New",monospace !important; diff --git a/webui/src/assets/main.css b/webui/src/assets/main.css index 26db94c7c..d2f013553 100644 --- a/webui/src/assets/main.css +++ b/webui/src/assets/main.css @@ -27,17 +27,25 @@ body { } #app { - display: flex; - flex-direction: column; + display: grid; + grid-template-columns: auto; + grid-template-areas: + "hd" + "main" + "ft"; min-height: 100%; } -#app:has(header .promo-banner) { - --header-height: calc(4rem + 22px); +header { + grid-area: hd; } main { - flex: 1; + grid-area: main; +} + +#app:has(header .promo-banner) { + --header-height: calc(4rem + 22px); } h1 { diff --git a/webui/src/components/Footer.vue b/webui/src/components/Footer.vue index 3d8390bc9..bd4ce8f49 100644 --- a/webui/src/components/Footer.vue +++ b/webui/src/components/Footer.vue @@ -127,4 +127,31 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/webui/src/views/DocsView.vue b/webui/src/views/DocsView.vue index b79e287bb..0b4a55e6c 100644 --- a/webui/src/views/DocsView.vue +++ b/webui/src/views/DocsView.vue @@ -177,27 +177,23 @@ async function copyToClipboard(event: MouseEvent) { \ No newline at end of file diff --git a/webui/src/components/NavDocItem.vue b/webui/src/components/NavDocItem.vue new file mode 100644 index 000000000..a3cfc74f3 --- /dev/null +++ b/webui/src/components/NavDocItem.vue @@ -0,0 +1,80 @@ + + + \ No newline at end of file diff --git a/webui/src/components/dashboard/Config.vue b/webui/src/components/dashboard/Config.vue index 9aa184094..63f1de155 100644 --- a/webui/src/components/dashboard/Config.vue +++ b/webui/src/components/dashboard/Config.vue @@ -60,9 +60,9 @@ const provider = computed(() => {
- +
diff --git a/webui/src/components/dashboard/Search.vue b/webui/src/components/dashboard/Search.vue index 4bdb77cff..f92702d30 100644 --- a/webui/src/components/dashboard/Search.vue +++ b/webui/src/components/dashboard/Search.vue @@ -264,7 +264,7 @@ function facetTitle(s: string) {
  • (get OR post) AND pets – Combine multiple terms logically
  • - Learn more about Mokapi's search here + Learn more about Mokapi's search here
    diff --git a/webui/src/components/dashboard/mail/MailFooter.vue b/webui/src/components/dashboard/mail/MailFooter.vue index e2fd9d7c1..6a068a7f4 100644 --- a/webui/src/components/dashboard/mail/MailFooter.vue +++ b/webui/src/components/dashboard/mail/MailFooter.vue @@ -10,7 +10,7 @@ defineProps({ \ No newline at end of file diff --git a/webui/src/components/docs/DocNav.vue b/webui/src/components/docs/DocNav.vue index 2a34e0252..c1289c0ac 100644 --- a/webui/src/components/docs/DocNav.vue +++ b/webui/src/components/docs/DocNav.vue @@ -1,119 +1,70 @@ @@ -130,64 +81,4 @@ function isExpanded(item: DocEntry | string) { border-bottom-style: solid; border-bottom-width: 1px; } -nav { - line-height: 1.5; - font-size: 0.95rem; -} -.nav, .nav .btn { - font-size: var(--bs-nav-link-font-size) -} -.nav-title { - font-size: var(--bs-nav-link-font-size) -} - -.nav-item a, .subchapter .btn-link, .nav-item .chapter > div { - padding-top: 7px; - padding-bottom: 7px; -} - -.nav-item .chapter > div a { - padding-top: 0; - padding-bottom: 0; -} - -.nav .nav-link { - padding-left: 0; -} - -@media only screen and (max-width: 768px) { - .nav a { - padding-top: 10px; - padding-bottom: 10px; - } -} - -.nav .active { - color: var(--nav-color-active); -} - -.nav button { - color: var(--color-text); - padding: 0; - text-decoration: none; - border: 0; -} - -.nav button:hover { - color: var(--nav-color-active); -} - -.nav button[aria-expanded=false] .bi-caret-up-fill { - display: none; -} - -.nav button[aria-expanded=true] .bi-caret-down-fill { - display: none; -} -.image.shadow { - border-radius: 8px; - box-shadow: - 0px 10px 0.5rem rgba(0, 0, 0, 0.3), - 10px 0px 0.5rem rgba(0, 0, 0, 0.3); -} \ No newline at end of file diff --git a/webui/src/components/docs/Examples.vue b/webui/src/components/docs/Examples.vue index 2b5836d01..f84d23854 100644 --- a/webui/src/components/docs/Examples.vue +++ b/webui/src/components/docs/Examples.vue @@ -2,48 +2,73 @@ import { computed, inject, ref } from 'vue'; import { parseMetadata } from '@/composables/markdown' import { useRoute, useRouter } from 'vue-router'; +import { useFileResolver } from '@/composables/file-resolver'; const files = inject>('files')! const nav = inject('nav')! const resources = nav['Resources']! -const exampleFiles = (resources.items!['Examples']).items ?? {} -const tutorialsFiles = (resources.items!['Tutorials']).items ?? {} -const blogFiles = (resources.items!['Blogs']).items ?? {} +const exampleFiles = computed(() => { + if (!resources.items) { + return [] as DocEntry[] + } + const examples = resources.items.find(x => x.label === 'Examples') + if (!examples || !examples.items) { + return [] as DocEntry[] + } + return examples.items +}) +const tutorialsFiles = computed(() => { + if (!resources.items) { + return [] as DocEntry[] + } + const tutorials = resources.items.find(x => x.label === 'Tutorials') + if (!tutorials || !tutorials.items) { + return [] as DocEntry[] + } + return tutorials.items +}) +const blogFiles = computed(() => { + if (!resources.items) { + return [] as DocEntry[] + } + const blogs = resources.items.find(x => x.label === 'Blogs') + if (!blogs || !blogs.items) { + return [] as DocEntry[] + } + return blogs.items +}) const type = ref('all') const tech = ref('all') const router = useRouter() const route = useRoute() -if (route.params.level2) { - const level2 = route.params.level2 - type.value = level2.substring(0, level2.length-1) +if (route.params.level1) { + const level1 = route.params.level1 + type.value = level1.substring(0, level1.length-1) } const items = computed(() => { const items = [] - for (const key in exampleFiles) { - const file = exampleFiles[key] - const meta = parseMetadata(files[`/src/assets/docs/${file}`]!) - items.push({ key: key, meta: meta, tag: 'example', level2: 'examples' }) + for (const item of exampleFiles.value) { + const meta = parseMetadata(files[`/src/assets/docs/${item.source}`]!) + items.push({ label: item, path: item.path, meta: meta, tag: 'example' }) } - for (const key in tutorialsFiles) { - const file = tutorialsFiles[key] - const meta = parseMetadata(files[`/src/assets/docs/${file}`]!) - items.push({ key: key, meta: meta, tag: 'tutorial', level2: 'tutorials' }) + for (const item of tutorialsFiles.value) { + const meta = parseMetadata(files[`/src/assets/docs/${item.source}`]!) + items.push({ label: item, path: item.path, meta: meta, tag: 'tutorial' }) } - for (const key in blogFiles) { - const file = blogFiles[key] - const meta = parseMetadata(files[`/src/assets/docs/${file}`]!) - items.push({ key: key, meta: meta, tag: 'blog', level2: 'blogs' }) + for (const item of blogFiles.value) { + const meta = parseMetadata(files[`/src/assets/docs/${item.source}`]!) + items.push({ label: item, path: item.path, meta: meta, tag: 'blog' }) } items.sort((x1, x2) => { if (!x1 || !x1.meta || !x1.meta.title) { - console.error('missing meta title for ' + x1.key) + console.error('missing meta title for ' + x1.label) } if (!x2 || !x2.meta || !x2.meta.title) { - console.error('missing meta title for ' + x2.key) + console.error('missing meta title for ' + x2.label) } return x1.meta.title.localeCompare(x2.meta.title) }) @@ -56,6 +81,7 @@ const filtered = computed(() => { } const filtered = [] + console.log(type.value) for (const item of items.value) { if ((type.value === 'all' || type.value === item.tag) && (tech.value === 'all' || tech.value === item.meta.tech || (tech.value === 'core' && !item.meta.tech))) { filtered.push(item) @@ -65,10 +91,6 @@ const filtered = computed(() => { return filtered }) -function formatParam(label: any): string { - return label.toString().toLowerCase().split(' ').join('-').split('/').join('-') -} - const state = computed<{ [name: string]: boolean }>(() => { return { tutorial: isTypeAvailable('tutorial'), @@ -128,9 +150,9 @@ function getTypeUrl(s: string) { tech.value = 'all' } if (s === 'all') { - return router.resolve({ params: { level2: '' } }).href + return router.resolve({ path: '/resources' }).href } else { - return router.resolve({ params: { level2: s + 's' } }).href + return router.resolve({ name: 'resources', params: { level1: s + 's' } }).href } } function setType(s: string) { @@ -204,7 +226,7 @@ function setType(s: string) { {{ item.meta.title }}
    {{ item.meta.description }}
    - + diff --git a/webui/src/composables/file-resolver.ts b/webui/src/composables/file-resolver.ts index 1092308c5..f0448eb28 100644 --- a/webui/src/composables/file-resolver.ts +++ b/webui/src/composables/file-resolver.ts @@ -1,80 +1,56 @@ import type { RouteLocationNormalizedLoaded } from "vue-router" -const MAX_LEVEL = 4 - export function useFileResolver() { - - function resolve(config: DocConfig, route: RouteLocationNormalizedLoaded) { - let level1 = route.params.level1 - if (!level1) { - return { file: null, levels: [] } - } - let file: DocEntry | undefined | string; - ({ name: level1, file: file} = select(config, level1)) - if (!file) { - return { file, levels: [] } - } - const levels = [ level1 ] - let isIndex: boolean | undefined = false - for (let index = 2; index <= MAX_LEVEL; index++) { - let level = route.params[`level${index}`]; - if (!file || typeof file === 'string') { - break - } - ({ level: level, file, isIndex } = getLevel(level, file)) - if (!level) { - break + function resolve(config: DocConfig, route: RouteLocationNormalizedLoaded): DocEntry | undefined { + for (const name of Object.keys(config)) { + const entry = getEntries(config[name]!, (e) => e.path?.toLocaleLowerCase() === route.path) + if (entry) { + return entry[entry.length-1] } - levels.push(level) } - - return { file, levels, isIndex } + return undefined } - function getLevel(level: string, file: DocEntry | string) { - let isIndex = false - if (typeof file !== 'string' && file.items) { - if (!level) { - if (file.index) { - isIndex = true - file = file.index - } else { - // get first element as 'index' file - level = Object.keys(file.items)[0]!; - const found = find(level, file) - return { level, file: found.file } + function getEntries(entry: DocEntry, check: (entry: DocEntry) => boolean): DocEntry[] | undefined { + if (check(entry)) { + return [entry] + } + + if (entry.items) { + for (const item of entry.items) { + const items = getEntries(item, check) + if (!items) { + continue } - } else{ - const found = find(level, file) - return { level: found.name, file: found.file } + return [entry, ...items] } } - return { level, file, isIndex } + return undefined } - function find(name: string, config: DocEntry) { - name = getField(config.items, name) - return { name: name, file: config.items![name] } - } - - function select(obj: DocConfig, name: string) { - name = getField(obj, name) - return { name: name, file: obj[name] } - } - - function getField(obj: any, name: string) { - const searchFor = name.toLowerCase().replaceAll(/[-]/g, ' ') - return Object.keys(obj).find( - key => key.toLowerCase().replaceAll(/[\/]/g, ' ') === searchFor)! + function getBreadcrumb(config: DocConfig, route: RouteLocationNormalizedLoaded): DocEntry[] | undefined { + for (const name of Object.keys(config)) { + const entries = getEntries(config[name]!, (e) => e.path === route.path) + if (entries) { + entries[0] = Object.assign({ label: name }, entries[0]) + return entries + } + } + return undefined } - function isKnown(config: DocConfig, level: string): boolean { - if (getField(config, level)) { - return true + function getEntryBySource(config: DocConfig, source: string) { + for (const name of Object.keys(config)) { + const entry = getEntries(config[name]!, (e) => { + return e.source === source + }) + if (entry) { + return entry[entry.length-1] + } } - return false + return undefined } - return { resolve, isKnown } + return { resolve, getBreadcrumb, getEntryBySource } } \ No newline at end of file diff --git a/webui/src/router/index.ts b/webui/src/router/index.ts index 1591d5692..2efd6ac62 100644 --- a/webui/src/router/index.ts +++ b/webui/src/router/index.ts @@ -280,26 +280,36 @@ const router = createRouter({ path: '/docs/examples/:pathMatch(.*)*', redirect: to => { if (typeof to.params.pathMatch === 'string') { - return `/docs/resources/${to.params.pathMatch}` + return `/resources/${to.params.pathMatch}` } - return `/docs/resources/${to.params.pathMatch!.join('/')}` + return `/resources/${to.params.pathMatch!.join('/')}` } }, { path: '/docs', redirect: ({ name: 'docs', - params: {level1: 'guides', level2: 'welcome'} + params: { level1: 'welcome' } }), - name: 'docsStart', - children: [ + children: [ { - path: '/docs/:level1/:level2?/:level3?/:level4?', + path: ':level1/:level2?/:level3?/:level4?', name: 'docs', component: () => import('@/views/DocsView.vue') }, ] }, + { + path: '/resources', + component: () => import('@/views/DocsView.vue'), + children: [ + { + path: ':level1/:level2?/:level3?/:level4?', + name: 'resources', + component: () => import('@/views/DocsView.vue') + }, + ] + }, { path: '/:pathMatch(.*)*', name: 'not-found', diff --git a/webui/src/types/index.d.ts b/webui/src/types/index.d.ts index e72bc1e62..29a8ecf81 100644 --- a/webui/src/types/index.d.ts +++ b/webui/src/types/index.d.ts @@ -1,18 +1,24 @@ -interface DocConfig{ +interface DocConfig { [name: string]: DocEntry } interface DocEntry { + label: string + source?: string + path?: string + index?: DocEntry + items?: DocEntry[] + type?: 'headline' | 'root' + expanded?: boolean - hideNavigation: boolean - hideInNavigation: boolean + hideNavigation?: boolean + hideInNavigation?: boolean canonical?: string + component?: string - index?: DocEntry - items?: {[name: string]: string | DocEntry } title?: string - description: string + description?: string } interface DocMeta { diff --git a/webui/src/views/DashboardView.vue b/webui/src/views/DashboardView.vue index 90787e1f2..bf70787f5 100644 --- a/webui/src/views/DashboardView.vue +++ b/webui/src/views/DashboardView.vue @@ -109,8 +109,8 @@ useMeta('Dashboard | mokapi.io', description, '')
    -
    -

    Dashboard

    +
    +

    Dashboard

    Demo Dashboard

    @@ -226,11 +226,11 @@ useMeta('Dashboard | mokapi.io', description, '')

    -
    - - - + + + +
    diff --git a/webui/src/views/DocsView.vue b/webui/src/views/DocsView.vue index 2d4415af4..332aa785f 100644 --- a/webui/src/views/DocsView.vue +++ b/webui/src/views/DocsView.vue @@ -1,6 +1,6 @@