mirror of
https://github.com/kunkunsh/kunkun.git
synced 2025-04-12 17:59:43 +00:00
UI Updates (#246)
* minor ui updates to shiki * feat: add markdown renderer * feat(ui): add scroll area component and expand markdown renderer * feat(ui): expand markdown syntax highlighting with additional language support * feat(ui): add markdown language support to syntax highlighting * feat(ui): update markdown syntax highlighting theme to GitHub Dark Default * feat(ui): add bash language support to markdown syntax highlighting * feat: add globe component * Change RetroGrid bg color * feat: add headless command list to store detail component * feat: update markdown renderer Replace svelte-markdown with svelte-exmarkdown, with custom tauri link renderer and code highlight support * format and fix eslint
This commit is contained in:
parent
cd7301255b
commit
310969e597
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@kksh/desktop",
|
"name": "@kksh/desktop",
|
||||||
"version": "0.1.33",
|
"version": "0.1.34",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -86,42 +86,22 @@ class ExtensionTemplate extends TemplateUiCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
|
return ui.render(
|
||||||
|
new Markdown(`
|
||||||
|
# Hello World
|
||||||
|
|
||||||
|
[GitHub HuakunShen](https://github.com/HuakunShen)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<img src="https://github.com/huakunshen.png" />
|
||||||
|
|
||||||
|
`)
|
||||||
|
)
|
||||||
ui.setSearchBarPlaceholder("Search for items")
|
ui.setSearchBarPlaceholder("Search for items")
|
||||||
const sections = getSections(2)
|
const sections = getSections(2)
|
||||||
const items = getItems(5)
|
const items = getItems(5)
|
||||||
return ui.render(
|
|
||||||
new List.List({
|
|
||||||
items: items.map(
|
|
||||||
(item) =>
|
|
||||||
new List.Item({
|
|
||||||
title: item.name,
|
|
||||||
value: item.id
|
|
||||||
// icon: new Icon({
|
|
||||||
// type: IconType.enum.Iconify,
|
|
||||||
// value: "mingcute:appstore-fill"
|
|
||||||
// })
|
|
||||||
})
|
|
||||||
),
|
|
||||||
sections: sections.map(
|
|
||||||
(section) =>
|
|
||||||
new List.Section({
|
|
||||||
title: section.name,
|
|
||||||
items: section.items.map(
|
|
||||||
(item) =>
|
|
||||||
new List.Item({
|
|
||||||
title: item.name,
|
|
||||||
value: item.id
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
ui.showLoadingBar(true)
|
ui.showLoadingBar(true)
|
||||||
setTimeout(() => {
|
|
||||||
ui.showLoadingBar(false)
|
|
||||||
clipboard.paste()
|
|
||||||
}, 2000)
|
|
||||||
const extPath = await path.extensionDir()
|
const extPath = await path.extensionDir()
|
||||||
const cmd = shell.createCommand("deno", ["run", "/Users/hk/Dev/kunkun/deno.ts"])
|
const cmd = shell.createCommand("deno", ["run", "/Users/hk/Dev/kunkun/deno.ts"])
|
||||||
cmd.stdout.on("data", (data) => {
|
cmd.stdout.on("data", (data) => {
|
||||||
@ -189,10 +169,14 @@ class ExtensionTemplate extends TemplateUiCommand {
|
|||||||
]),
|
]),
|
||||||
new Markdown(`
|
new Markdown(`
|
||||||
# Hello World
|
# Hello World
|
||||||
|
|
||||||
|
[GitHub HuakunShen](https://github.com/HuakunShen)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
<img src="https://github.com/huakunshen.png" />
|
<img src="https://github.com/huakunshen.png" />
|
||||||
<img src="https://github.com/huakunshen.png" />
|
|
||||||
<img src="https://github.com/huakunshen.png" />
|
`)
|
||||||
`)
|
|
||||||
],
|
],
|
||||||
width: 50
|
width: 50
|
||||||
}),
|
}),
|
||||||
|
@ -32,6 +32,10 @@
|
|||||||
"./animation": {
|
"./animation": {
|
||||||
"types": "./src/components/animation/index.ts",
|
"types": "./src/components/animation/index.ts",
|
||||||
"svelte": "./src/components/animation/index.ts"
|
"svelte": "./src/components/animation/index.ts"
|
||||||
|
},
|
||||||
|
"./markdown": {
|
||||||
|
"types": "./src/components/markdown/index.ts",
|
||||||
|
"svelte": "./src/components/markdown/index.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -76,17 +80,24 @@
|
|||||||
"@internationalized/date": "^3.7.0",
|
"@internationalized/date": "^3.7.0",
|
||||||
"@kksh/supabase": "workspace:*",
|
"@kksh/supabase": "workspace:*",
|
||||||
"@shikijs/langs": "^2.3.2",
|
"@shikijs/langs": "^2.3.2",
|
||||||
|
"@shikijs/rehype": "^3.2.1",
|
||||||
"@shikijs/themes": "^2.3.2",
|
"@shikijs/themes": "^2.3.2",
|
||||||
"@std/semver": "npm:@jsr/std__semver@^1.0.3",
|
"@std/semver": "npm:@jsr/std__semver@^1.0.3",
|
||||||
"@tanstack/svelte-virtual": "^3.13.2",
|
"@tanstack/svelte-virtual": "^3.13.2",
|
||||||
|
"cobe": "^0.6.3",
|
||||||
"dompurify": "^3.2.3",
|
"dompurify": "^3.2.3",
|
||||||
"fuse.js": "^7.1.0",
|
"fuse.js": "^7.1.0",
|
||||||
"gsap": "^3.12.7",
|
"gsap": "^3.12.7",
|
||||||
|
"katex": "^0.16.21",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"pretty-bytes": "^6.1.1",
|
"pretty-bytes": "^6.1.1",
|
||||||
|
"rehype-class-names": "^2.0.0",
|
||||||
|
"rehype-katex": "^7.0.1",
|
||||||
|
"rehype-raw": "^7.0.0",
|
||||||
|
"remark-math": "^6.0.0",
|
||||||
"shiki-magic-move": "^0.5.2",
|
"shiki-magic-move": "^0.5.2",
|
||||||
|
"svelte-exmarkdown": "^4.0.3",
|
||||||
"svelte-inspect-value": "^0.3.0",
|
"svelte-inspect-value": "^0.3.0",
|
||||||
"svelte-markdown": "^0.4.1",
|
|
||||||
"svelte-motion": "^0.12.2",
|
"svelte-motion": "^0.12.2",
|
||||||
"valibot": "1.0.0-beta.12"
|
"valibot": "1.0.0-beta.12"
|
||||||
}
|
}
|
||||||
|
120
packages/ui/src/components/animation/Globe.svelte
Normal file
120
packages/ui/src/components/animation/Globe.svelte
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import createGlobe from "cobe"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
import { Spring, spring } from "svelte/motion"
|
||||||
|
import { cn } from "../../utils"
|
||||||
|
|
||||||
|
let x = spring(0, {
|
||||||
|
stiffness: 0.04,
|
||||||
|
damping: 0.4,
|
||||||
|
precision: 0.005
|
||||||
|
})
|
||||||
|
|
||||||
|
// let className = ""
|
||||||
|
// export { className as class }
|
||||||
|
let { locations = [], class: className }: { class?: string; locations?: [number, number][] } =
|
||||||
|
$props()
|
||||||
|
let pointerInteracting: number | null = null
|
||||||
|
let pointerInteractionMovement = 0
|
||||||
|
let canvas: HTMLCanvasElement
|
||||||
|
|
||||||
|
let phi = 0
|
||||||
|
let width = 0
|
||||||
|
// $: console.log(width, "X")
|
||||||
|
let onResize = () => {
|
||||||
|
width = canvas.offsetWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
let onRender = (state: Record<string, any>) => {
|
||||||
|
if (!pointerInteracting) {
|
||||||
|
phi += 0.005
|
||||||
|
}
|
||||||
|
state.phi = phi + $x
|
||||||
|
state.width = width * 2
|
||||||
|
state.height = width * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
// Adds the resize event listener when the component is mounted
|
||||||
|
window.addEventListener("resize", onResize)
|
||||||
|
onResize()
|
||||||
|
|
||||||
|
// Initializes the globe with specific options
|
||||||
|
const globe = createGlobe(canvas, {
|
||||||
|
devicePixelRatio: 2,
|
||||||
|
width: width,
|
||||||
|
height: width,
|
||||||
|
phi: 0,
|
||||||
|
theta: 0.3,
|
||||||
|
dark: 1,
|
||||||
|
diffuse: 0.4, // 1.2
|
||||||
|
mapSamples: 16000,
|
||||||
|
mapBrightness: 1.2, // 6
|
||||||
|
baseColor: [0.3, 0.3, 0.3],
|
||||||
|
markerColor: [251 / 255, 100 / 255, 21 / 255],
|
||||||
|
glowColor: [1, 1, 1],
|
||||||
|
markers: locations.map((location) => {
|
||||||
|
return {
|
||||||
|
location: location,
|
||||||
|
size: 0.03
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
// [
|
||||||
|
// { location: [14.5995, 120.9842], size: 0.03 },
|
||||||
|
// { location: [19.076, 72.8777], size: 0.03 },
|
||||||
|
// { location: [23.8103, 90.4125], size: 0.05 },
|
||||||
|
// { location: [30.0444, 31.2357], size: 0.07 },
|
||||||
|
// { location: [39.9042, 116.4074], size: 0.08 },
|
||||||
|
// { location: [-23.5505, -46.6333], size: 0.05 },
|
||||||
|
// { location: [19.4326, -99.1332], size: 0.04 },
|
||||||
|
// { location: [40.7128, -74.006], size: 0.1 },
|
||||||
|
// { location: [34.6937, 135.5022], size: 0.05 },
|
||||||
|
// { location: [41.0082, 28.9784], size: 0.06 }
|
||||||
|
// ],
|
||||||
|
// onRender: (state) => {
|
||||||
|
// if (!pointerInteracting) {
|
||||||
|
// // Called on every animation frame.
|
||||||
|
// // `state` will be an empty object, return updated params.
|
||||||
|
// phi += 0.009;
|
||||||
|
// }
|
||||||
|
// state.phi = phi + $x;
|
||||||
|
|
||||||
|
// // phi += 0.01;
|
||||||
|
// },
|
||||||
|
onRender: onRender
|
||||||
|
})
|
||||||
|
|
||||||
|
// Removes the resize event listener when the component is unmounted to prevent memory leaks
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("resize", onResize)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main class={cn("absolute inset-0 mx-auto aspect-[1/1] w-full max-w-[600px]", className)}>
|
||||||
|
<canvas
|
||||||
|
class="h-full w-full [contain:layout_paint_size]"
|
||||||
|
bind:this={canvas}
|
||||||
|
onpointerdown={(e) => {
|
||||||
|
pointerInteracting = e.clientX - pointerInteractionMovement
|
||||||
|
canvas.style.cursor = "grabbing"
|
||||||
|
}}
|
||||||
|
onpointerup={() => {
|
||||||
|
pointerInteracting = null
|
||||||
|
canvas.style.cursor = "grab"
|
||||||
|
}}
|
||||||
|
onpointerout={() => {
|
||||||
|
pointerInteracting = null
|
||||||
|
canvas.style.cursor = "grab"
|
||||||
|
}}
|
||||||
|
onmousemove={(e) => {
|
||||||
|
if (pointerInteracting !== null) {
|
||||||
|
console.log("working")
|
||||||
|
const delta = e.clientX - pointerInteracting
|
||||||
|
pointerInteractionMovement = delta
|
||||||
|
x.set(delta / 200)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
></canvas>
|
||||||
|
</main>
|
@ -2,10 +2,12 @@
|
|||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { Motion, useMotionTemplate, useMotionValue } from "svelte-motion"
|
import { Motion, useMotionTemplate, useMotionValue } from "svelte-motion"
|
||||||
import { cn } from "../../utils"
|
import { cn } from "../../utils"
|
||||||
|
import BorderBeam from "./BorderBeam.svelte"
|
||||||
|
|
||||||
export let gradientSize: number = 200
|
export let gradientSize: number = 200
|
||||||
export let gradientColor: string = "#262626"
|
export let gradientColor: string = "#262626"
|
||||||
export let gradientOpacity: number = 0.8
|
export let gradientOpacity: number = 0.8
|
||||||
|
export let borderBeam: boolean = false
|
||||||
let className: string = ""
|
let className: string = ""
|
||||||
export { className as class }
|
export { className as class }
|
||||||
|
|
||||||
@ -42,6 +44,9 @@
|
|||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
{#if borderBeam}
|
||||||
|
<BorderBeam size={150} duration={12} />
|
||||||
|
{/if}
|
||||||
<div class="relative z-10">
|
<div class="relative z-10">
|
||||||
<!-- Default -->
|
<!-- Default -->
|
||||||
<slot>
|
<slot>
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
|
|
||||||
<!-- Background Gradient -->
|
<!-- Background Gradient -->
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 bg-gradient-to-t from-white to-transparent to-90% dark:from-black"
|
class={cn(
|
||||||
|
"dark:from-background absolute inset-0 bg-gradient-to-t from-white to-transparent to-90%"
|
||||||
|
)}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,3 +5,4 @@ export { default as RetroGrid } from "./RetroGrid.svelte"
|
|||||||
export { default as AuroraText } from "./AuroraText.svelte"
|
export { default as AuroraText } from "./AuroraText.svelte"
|
||||||
export { default as WordRotate } from "./WordRotate.svelte"
|
export { default as WordRotate } from "./WordRotate.svelte"
|
||||||
export { default as MagicCard } from "./MagicCard.svelte"
|
export { default as MagicCard } from "./MagicCard.svelte"
|
||||||
|
export { default as Globe } from "./Globe.svelte"
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
{#await highlighter2 then highlighter}
|
{#await highlighter2 then highlighter}
|
||||||
<ShikiMagicMove
|
<ShikiMagicMove
|
||||||
class={cn("", className)}
|
class={cn("p-3", className)}
|
||||||
{lang}
|
{lang}
|
||||||
theme={theme ?? "vitesse-dark"}
|
theme={theme ?? "vitesse-dark"}
|
||||||
{highlighter}
|
{highlighter}
|
||||||
|
@ -5,36 +5,46 @@
|
|||||||
import type { HTMLAttributes } from "svelte/elements"
|
import type { HTMLAttributes } from "svelte/elements"
|
||||||
import { open } from "tauri-plugin-shellx-api"
|
import { open } from "tauri-plugin-shellx-api"
|
||||||
|
|
||||||
const {
|
let {
|
||||||
href,
|
href,
|
||||||
class: className = "",
|
class: className = "",
|
||||||
children
|
style,
|
||||||
|
children,
|
||||||
|
ref = $bindable(null)
|
||||||
}: {
|
}: {
|
||||||
href: string
|
href?: string
|
||||||
|
style?: HTMLAttributes<HTMLAnchorElement>["style"]
|
||||||
class?: HTMLAttributes<HTMLAnchorElement>["class"]
|
class?: HTMLAttributes<HTMLAnchorElement>["class"]
|
||||||
children: Snippet
|
children: Snippet
|
||||||
|
ref: HTMLAnchorElement | HTMLButtonElement | null
|
||||||
} = $props()
|
} = $props()
|
||||||
|
|
||||||
// @ts-expect-error window.__TAURI_INTERNALS__ is not defined in the browser
|
// @ts-expect-error window.__TAURI_INTERNALS__ is not defined in the browser
|
||||||
const isInTauri = browser ? !!window.__TAURI_INTERNALS__ : false
|
const isInTauri = browser ? !!window.__TAURI_INTERNALS__ : false
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
|
if (href) {
|
||||||
open(href)
|
open(href)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if isInTauri}
|
{#if isInTauri}
|
||||||
<button
|
<button
|
||||||
|
bind:this={ref}
|
||||||
class={cn(
|
class={cn(
|
||||||
"text-left font-medium text-blue-600 hover:cursor-pointer hover:underline dark:text-blue-500",
|
"text-left font-medium text-blue-600 hover:cursor-pointer hover:underline dark:text-blue-500",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
{style}
|
||||||
onclick={handleClick}
|
onclick={handleClick}
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</button>
|
</button>
|
||||||
{:else}
|
{:else}
|
||||||
<a
|
<a
|
||||||
|
bind:this={ref}
|
||||||
{href}
|
{href}
|
||||||
|
{style}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class={cn(
|
class={cn(
|
||||||
"text-left font-medium text-blue-600 hover:cursor-pointer hover:underline dark:text-blue-500",
|
"text-left font-medium text-blue-600 hover:cursor-pointer hover:underline dark:text-blue-500",
|
||||||
|
@ -260,7 +260,7 @@
|
|||||||
<h2 class="text-lg font-bold">Commands</h2>
|
<h2 class="text-lg font-bold">Commands</h2>
|
||||||
<ul>
|
<ul>
|
||||||
{#if manifest}
|
{#if manifest}
|
||||||
{#each [...(manifest.customUiCmds ?? []), ...(manifest.templateUiCmds ?? [])] as cmd}
|
{#each [...(manifest.customUiCmds ?? []), ...(manifest.templateUiCmds ?? []), ...(manifest.headlessCmds ?? [])] as cmd}
|
||||||
<li>
|
<li>
|
||||||
<div class="flex items-center space-x-3">
|
<div class="flex items-center space-x-3">
|
||||||
{#if manifest}
|
{#if manifest}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { cn } from "@kksh/ui/utils"
|
import { cn } from "@kksh/ui/utils"
|
||||||
import SvelteMarkdown from "svelte-markdown"
|
import Markdown from "../../markdown/Markdown.svelte"
|
||||||
|
|
||||||
const { markdown, class: className }: { markdown: string; class?: string } = $props()
|
const { markdown, class: className }: { markdown: string; class?: string } = $props()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={cn("prose dark:prose-invert", className)}>
|
<div class={cn("prose dark:prose-invert", className)}>
|
||||||
<SvelteMarkdown source={markdown} />
|
<Markdown md={markdown} />
|
||||||
</div>
|
</div>
|
||||||
|
18
packages/ui/src/components/markdown/A.svelte
Normal file
18
packages/ui/src/components/markdown/A.svelte
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Button } from "@kksh/svelte5"
|
||||||
|
import { CopyIcon } from "lucide-svelte"
|
||||||
|
import type { Snippet } from "svelte"
|
||||||
|
import TauriLink from "../common/TauriLink.svelte"
|
||||||
|
|
||||||
|
let a: HTMLAnchorElement | HTMLButtonElement | null = $state(null)
|
||||||
|
|
||||||
|
let {
|
||||||
|
children,
|
||||||
|
class: className,
|
||||||
|
href,
|
||||||
|
style,
|
||||||
|
...rest
|
||||||
|
}: { children: Snippet; href?: string; class?: string; style?: string } = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<TauriLink {href} class={className} {style} bind:ref={a}>{@render children()}</TauriLink>
|
88
packages/ui/src/components/markdown/Markdown.svelte
Normal file
88
packages/ui/src/components/markdown/Markdown.svelte
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import rehypeShikiFromHighlighter from "@shikijs/rehype/core"
|
||||||
|
import rehypeClassNames from "rehype-class-names"
|
||||||
|
import rehypeKatex from "rehype-katex"
|
||||||
|
import rehypeRaw from "rehype-raw"
|
||||||
|
import remarkMath from "remark-math"
|
||||||
|
import { createHighlighterCoreSync } from "shiki/core"
|
||||||
|
import { createJavaScriptRegexEngine } from "shiki/engine/javascript"
|
||||||
|
import bash from "shiki/langs/bash.mjs"
|
||||||
|
import cpp from "shiki/langs/cpp.mjs"
|
||||||
|
import csharp from "shiki/langs/csharp.mjs"
|
||||||
|
import go from "shiki/langs/go.mjs"
|
||||||
|
import html from "shiki/langs/html.mjs"
|
||||||
|
import java from "shiki/langs/java.mjs"
|
||||||
|
import json from "shiki/langs/json.mjs"
|
||||||
|
import kotlin from "shiki/langs/kotlin.mjs"
|
||||||
|
import markdown from "shiki/langs/markdown.mjs"
|
||||||
|
import php from "shiki/langs/php.mjs"
|
||||||
|
import python from "shiki/langs/python.mjs"
|
||||||
|
import ruby from "shiki/langs/ruby.mjs"
|
||||||
|
import rust from "shiki/langs/rust.mjs"
|
||||||
|
import shell from "shiki/langs/shell.mjs"
|
||||||
|
import svelte from "shiki/langs/svelte.mjs"
|
||||||
|
import swift from "shiki/langs/swift.mjs"
|
||||||
|
import ts from "shiki/langs/typescript.mjs"
|
||||||
|
import yaml from "shiki/langs/yaml.mjs"
|
||||||
|
import githubDarkDefault from "shiki/themes/github-dark-default.mjs"
|
||||||
|
import Markdown from "svelte-exmarkdown"
|
||||||
|
import type { Plugin } from "svelte-exmarkdown"
|
||||||
|
import { gfmPlugin } from "svelte-exmarkdown/gfm"
|
||||||
|
import A from "./A.svelte"
|
||||||
|
import Pre from "./Pre.svelte"
|
||||||
|
|
||||||
|
const addClass: Plugin = {
|
||||||
|
rehypePlugin: [
|
||||||
|
rehypeClassNames,
|
||||||
|
{
|
||||||
|
pre: "p-4 rounded-md overflow-auto"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const shikiPlugin = {
|
||||||
|
rehypePlugin: [
|
||||||
|
rehypeShikiFromHighlighter,
|
||||||
|
createHighlighterCoreSync({
|
||||||
|
themes: [githubDarkDefault],
|
||||||
|
langs: [
|
||||||
|
ts,
|
||||||
|
svelte,
|
||||||
|
json,
|
||||||
|
html,
|
||||||
|
rust,
|
||||||
|
python,
|
||||||
|
java,
|
||||||
|
cpp,
|
||||||
|
csharp,
|
||||||
|
go,
|
||||||
|
ruby,
|
||||||
|
php,
|
||||||
|
markdown,
|
||||||
|
kotlin,
|
||||||
|
swift,
|
||||||
|
yaml,
|
||||||
|
shell,
|
||||||
|
bash
|
||||||
|
],
|
||||||
|
engine: createJavaScriptRegexEngine()
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
theme: "github-dark-default"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} satisfies Plugin
|
||||||
|
|
||||||
|
const plugins: Plugin[] = [
|
||||||
|
shikiPlugin,
|
||||||
|
gfmPlugin(),
|
||||||
|
{ rehypePlugin: [rehypeRaw] },
|
||||||
|
{ remarkPlugin: [remarkMath], rehypePlugin: [rehypeKatex] },
|
||||||
|
addClass,
|
||||||
|
{ renderer: { pre: Pre, a: A } }
|
||||||
|
]
|
||||||
|
|
||||||
|
let { md }: { md: string } = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Markdown {md} {plugins} />
|
25
packages/ui/src/components/markdown/Pre.svelte
Normal file
25
packages/ui/src/components/markdown/Pre.svelte
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Button } from "@kksh/svelte5"
|
||||||
|
import { CopyIcon } from "lucide-svelte"
|
||||||
|
import type { Snippet } from "svelte"
|
||||||
|
|
||||||
|
let pre: HTMLPreElement
|
||||||
|
|
||||||
|
let {
|
||||||
|
children,
|
||||||
|
class: className,
|
||||||
|
style
|
||||||
|
}: { children: Snippet; class?: string; style?: string } = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="relative">
|
||||||
|
<pre class={className} {style} bind:this={pre}>{@render children()}</pre>
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
variant="outline"
|
||||||
|
onclick={() => navigator.clipboard.writeText(pre.textContent ?? "")}
|
||||||
|
class="absolute right-2 top-2"
|
||||||
|
>
|
||||||
|
<CopyIcon />
|
||||||
|
</Button>
|
||||||
|
</div>
|
1
packages/ui/src/components/markdown/index.ts
Normal file
1
packages/ui/src/components/markdown/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as Markdown } from "./Markdown.svelte"
|
10
packages/ui/src/components/ui/scroll-area/index.ts
Normal file
10
packages/ui/src/components/ui/scroll-area/index.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import Scrollbar from "./scroll-area-scrollbar.svelte"
|
||||||
|
import Root from "./scroll-area.svelte"
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Scrollbar,
|
||||||
|
//,
|
||||||
|
Root as ScrollArea,
|
||||||
|
Scrollbar as ScrollAreaScrollbar
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { ScrollArea as ScrollAreaPrimitive, type WithoutChild } from "bits-ui"
|
||||||
|
import { cn } from "../../../utils"
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
orientation = "vertical",
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithoutChild<ScrollAreaPrimitive.ScrollbarProps> = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ScrollAreaPrimitive.Scrollbar
|
||||||
|
bind:ref
|
||||||
|
{orientation}
|
||||||
|
class={cn(
|
||||||
|
"flex touch-none select-none transition-colors",
|
||||||
|
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-px",
|
||||||
|
orientation === "horizontal" && "h-2.5 w-full border-t border-t-transparent p-px",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
<ScrollAreaPrimitive.Thumb
|
||||||
|
class={cn("bg-border relative rounded-full", orientation === "vertical" && "flex-1")}
|
||||||
|
/>
|
||||||
|
</ScrollAreaPrimitive.Scrollbar>
|
34
packages/ui/src/components/ui/scroll-area/scroll-area.svelte
Normal file
34
packages/ui/src/components/ui/scroll-area/scroll-area.svelte
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { ScrollArea as ScrollAreaPrimitive, type WithoutChild } from "bits-ui"
|
||||||
|
import { cn } from "../../../utils"
|
||||||
|
import { Scrollbar } from "./index.js"
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
viewportRef = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
orientation = "vertical",
|
||||||
|
scrollbarXClasses = "",
|
||||||
|
scrollbarYClasses = "",
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithoutChild<ScrollAreaPrimitive.RootProps> & {
|
||||||
|
viewportRef?: HTMLDivElement | null
|
||||||
|
orientation?: "vertical" | "horizontal" | "both" | undefined
|
||||||
|
scrollbarXClasses?: string | undefined
|
||||||
|
scrollbarYClasses?: string | undefined
|
||||||
|
} = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ScrollAreaPrimitive.Root bind:ref {...restProps} class={cn("relative overflow-hidden", className)}>
|
||||||
|
<ScrollAreaPrimitive.Viewport bind:ref={viewportRef} class="h-full w-full rounded-[inherit]">
|
||||||
|
{@render children?.()}
|
||||||
|
</ScrollAreaPrimitive.Viewport>
|
||||||
|
{#if orientation === "vertical" || orientation === "both"}
|
||||||
|
<Scrollbar orientation="vertical" class={scrollbarYClasses} />
|
||||||
|
{/if}
|
||||||
|
{#if orientation === "horizontal" || orientation === "both"}
|
||||||
|
<Scrollbar orientation="horizontal" class={scrollbarXClasses} />
|
||||||
|
{/if}
|
||||||
|
<ScrollAreaPrimitive.Corner />
|
||||||
|
</ScrollAreaPrimitive.Root>
|
@ -15,4 +15,5 @@ export { default as RetroGrid } from "./components/animation/RetroGrid.svelte"
|
|||||||
export { default as AuroraText } from "./components/animation/AuroraText.svelte"
|
export { default as AuroraText } from "./components/animation/AuroraText.svelte"
|
||||||
export * as Constants from "./constants"
|
export * as Constants from "./constants"
|
||||||
export * as Form from "./components/ui/form"
|
export * as Form from "./components/ui/form"
|
||||||
|
export * as ScrollArea from "./components/ui/scroll-area"
|
||||||
export { default as ModeToggle } from "./components/theme/mode-toggle.svelte"
|
export { default as ModeToggle } from "./components/theme/mode-toggle.svelte"
|
||||||
|
1003
pnpm-lock.yaml
generated
1003
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user