From 6f8107249f1f61ac1a3b3a97f89d1fad0e934467 Mon Sep 17 00:00:00 2001 From: rzmk <30333942+rzmk@users.noreply.github.com> Date: Mon, 15 Dec 2025 12:10:37 -0500 Subject: [PATCH] feat: add highlights and underlines to first doc page, fix OpenAPI CSS --- docs/app/global.css | 1 + docs/bun.lock | 12 ++++ docs/components/ui/highlighter.tsx | 103 +++++++++++++++++++++++++++++ docs/content/docs/index.mdx | 21 +++++- docs/package.json | 2 + 5 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 docs/components/ui/highlighter.tsx diff --git a/docs/app/global.css b/docs/app/global.css index a2af3e8..a8dba0c 100644 --- a/docs/app/global.css +++ b/docs/app/global.css @@ -2,6 +2,7 @@ @import 'fumadocs-ui/css/neutral.css'; @import "fumadocs-ui/css/ocean.css"; @import "fumadocs-ui/css/preset.css"; +@import 'fumadocs-openapi/css/preset.css'; @import "tw-animate-css"; @custom-variant dark (&:is(.dark *)); diff --git a/docs/bun.lock b/docs/bun.lock index d7a907f..fe80803 100644 --- a/docs/bun.lock +++ b/docs/bun.lock @@ -13,9 +13,11 @@ "fumadocs-openapi": "^10.1.4", "fumadocs-ui": "16.2.5", "lucide-react": "^0.561.0", + "motion": "^12.23.26", "next": "^16.0.10", "react": "^19.2.3", "react-dom": "^19.2.3", + "rough-notation": "^0.5.1", "shiki": "^3.20.0", "tailwind-merge": "^3.4.0", }, @@ -459,6 +461,8 @@ "foreach": ["foreach@2.0.6", "", {}, "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg=="], + "framer-motion": ["framer-motion@12.23.26", "", { "dependencies": { "motion-dom": "^12.23.23", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-cPcIhgR42xBn1Uj+PzOyheMtZ73H927+uWPDVhUMqxy8UHt6Okavb6xIz9J/phFUHUj0OncR6UvMfJTXoc/LKA=="], + "fumadocs-core": ["fumadocs-core@16.2.5", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.6.2", "@orama/orama": "^3.1.16", "@shikijs/rehype": "^3.20.0", "@shikijs/transformers": "^3.20.0", "estree-util-value-to-estree": "^3.5.0", "github-slugger": "^2.0.0", "hast-util-to-estree": "^3.1.3", "hast-util-to-jsx-runtime": "^2.3.6", "image-size": "^2.0.2", "negotiator": "^1.0.0", "npm-to-yarn": "^3.0.1", "path-to-regexp": "^8.3.0", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^3.20.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "@mixedbread/sdk": "^0.19.0", "@orama/core": "1.x.x", "@tanstack/react-router": "1.x.x", "@types/react": "*", "algoliasearch": "5.x.x", "lucide-react": "*", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router": "7.x.x", "waku": "^0.26.0 || ^0.27.0", "zod": "*" }, "optionalPeers": ["@mixedbread/sdk", "@orama/core", "@tanstack/react-router", "@types/react", "algoliasearch", "lucide-react", "next", "react", "react-dom", "react-router", "waku", "zod"] }, "sha512-u07n2oQJ2XaEQpWOdCyJnICYEasQiZhTFNf40C+Q2AJ3kKFeiz42mHccea0t/sjfBbO9pEDHyvZVHhSf/Cm3AA=="], "fumadocs-mdx": ["fumadocs-mdx@14.1.0", "", { "dependencies": { "@mdx-js/mdx": "^3.1.1", "@standard-schema/spec": "^1.0.0", "chokidar": "^5.0.0", "esbuild": "^0.27.1", "estree-util-value-to-estree": "^3.5.0", "js-yaml": "^4.1.1", "mdast-util-to-markdown": "^2.1.2", "picocolors": "^1.1.1", "picomatch": "^4.0.3", "remark-mdx": "^3.1.1", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3", "zod": "^4.1.13" }, "peerDependencies": { "@fumadocs/mdx-remote": "^1.4.0", "fumadocs-core": "^15.0.0 || ^16.0.0", "next": "^15.3.0 || ^16.0.0", "react": "*", "vite": "6.x.x || 7.x.x" }, "optionalPeers": ["@fumadocs/mdx-remote", "next", "react", "vite"], "bin": { "fumadocs-mdx": "dist/bin.js" } }, "sha512-6I3nXzM3+dSap5UZvKFQvOaKNKdMfxK5/8Cyu3am6zm0d/acuUxT1r1s1GQpc8H5iB9bFMtwyoZff1WN2qWq8g=="], @@ -649,6 +653,12 @@ "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + "motion": ["motion@12.23.26", "", { "dependencies": { "framer-motion": "^12.23.26", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-Ll8XhVxY8LXMVYTCfme27WH2GjBrCIzY4+ndr5QKxsK+YwCtOi2B/oBi5jcIbik5doXuWT/4KKDOVAZJkeY5VQ=="], + + "motion-dom": ["motion-dom@12.23.23", "", { "dependencies": { "motion-utils": "^12.23.6" } }, "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA=="], + + "motion-utils": ["motion-utils@12.23.6", "", {}, "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], @@ -729,6 +739,8 @@ "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + "rough-notation": ["rough-notation@0.5.1", "", {}, "sha512-ITHofTzm13cWFVfoGsh/4c/k2Mg8geKgBCwex71UZLnNuw403tCRjYPQ68jSAd37DMbZIePXPjDgY0XdZi9HPw=="], + "sax": ["sax@1.4.3", "", {}, "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ=="], "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], diff --git a/docs/components/ui/highlighter.tsx b/docs/components/ui/highlighter.tsx new file mode 100644 index 0000000..7ad0236 --- /dev/null +++ b/docs/components/ui/highlighter.tsx @@ -0,0 +1,103 @@ +"use client" + +import { useEffect, useRef } from "react" +import type React from "react" +import { useInView } from "motion/react" +import { annotate } from "rough-notation" +import { type RoughAnnotation } from "rough-notation/lib/model" + +type AnnotationAction = + | "highlight" + | "underline" + | "box" + | "circle" + | "strike-through" + | "crossed-off" + | "bracket" + +interface HighlighterProps { + children: React.ReactNode + action?: AnnotationAction + color?: string + strokeWidth?: number + animationDuration?: number + iterations?: number + padding?: number + multiline?: boolean + isView?: boolean +} + +export function Highlighter({ + children, + action = "highlight", + color = "#ffd1dc", + strokeWidth = 1.5, + animationDuration = 600, + iterations = 2, + padding = 2, + multiline = true, + isView = false, +}: HighlighterProps) { + const elementRef = useRef(null) + const annotationRef = useRef(null) + + const isInView = useInView(elementRef, { + once: true, + margin: "-10%", + }) + + // If isView is false, always show. If isView is true, wait for inView + const shouldShow = !isView || isInView + + useEffect(() => { + if (!shouldShow) return + + const element = elementRef.current + if (!element) return + + const annotationConfig = { + type: action, + color, + strokeWidth, + animationDuration, + iterations, + padding, + multiline, + } + + const annotation = annotate(element, annotationConfig) + + annotationRef.current = annotation + annotationRef.current.show() + + const resizeObserver = new ResizeObserver(() => { + annotation.hide() + annotation.show() + }) + + resizeObserver.observe(element) + resizeObserver.observe(document.body) + + return () => { + if (element) { + annotate(element, { type: action }).remove() + resizeObserver.disconnect() + } + } + }, [ + shouldShow, + action, + color, + strokeWidth, + animationDuration, + iterations, + padding, + multiline, + ]) + + return ( + + {children} + + ) +} diff --git a/docs/content/docs/index.mdx b/docs/content/docs/index.mdx index d5a3929..0d518f3 100644 --- a/docs/content/docs/index.mdx +++ b/docs/content/docs/index.mdx @@ -2,7 +2,9 @@ title: ckanaction docs --- -ckanaction is a Rust library crate that acts as an API wrapper for the CKAN Actions API (v3). +import { Highlighter } from "@/components/ui/highlighter" + +ckanaction is a Rust library crate that acts as an API wrapper for the CKAN Actions API (v3). There is also an interactive web GUI. This means that instead of using generic request library crates such as `reqwest`, a developer can use `ckanaction` instead to make API calls to their CKAN instance. @@ -35,8 +37,21 @@ async fn main() -> Result<(), Box> { The source code of ckanaction can be found at [github.com/dathere/ckanaction](https://github.com/ckanaction). -You may also explore this web app to view more code examples for each endpoint and also use an interactive GUI for sending HTTP requests to any local or remote CKAN instance. +You may also explore this web app to view more code examples for each endpoint and also use an interactive GUI for sending HTTP requests to any local or remote CKAN instance. -View the GIF below to see how to point to a specific CKAN API endpoint using the interactive GUI, which by default points to a local CKAN instance endpoint's URL. +View the GIF below to see how to point to a specific CKAN API endpoint using the interactive GUI, which by default points to a local CKAN instance endpoint's URL. ![ckanaction web app demo](/ckanaction-web-app-interactive-demo.gif) + +import { GitMergeIcon, MousePointerClickIcon } from 'lucide-react'; + + + } + href="/docs/status_show" + title="Try out an endpoint!" + > + Explore the web GUI by using the `status_show` CKAN Actions API endpoint. + + } href="https://github.com/dathere/ckanaction" title="Source code">Explore the source code for the Rust library crate and web GUI. + diff --git a/docs/package.json b/docs/package.json index 32bd049..df5bb54 100644 --- a/docs/package.json +++ b/docs/package.json @@ -19,9 +19,11 @@ "fumadocs-openapi": "^10.1.4", "fumadocs-ui": "16.2.5", "lucide-react": "^0.561.0", + "motion": "^12.23.26", "next": "^16.0.10", "react": "^19.2.3", "react-dom": "^19.2.3", + "rough-notation": "^0.5.1", "shiki": "^3.20.0", "tailwind-merge": "^3.4.0" },