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" },