This commit is contained in:
jasinco 2024-11-04 01:18:21 +08:00
parent 739dafc2c1
commit 82d7d09818
33 changed files with 4597 additions and 96 deletions

View File

@ -3,7 +3,10 @@
"singleQuote": true, "singleQuote": true,
"trailingComma": "none", "trailingComma": "none",
"printWidth": 100, "printWidth": 100,
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], "plugins": [
"prettier-plugin-svelte",
"prettier-plugin-tailwindcss"
],
"overrides": [ "overrides": [
{ {
"files": "*.svelte", "files": "*.svelte",

1
.yarnrc.yml Normal file
View File

@ -0,0 +1 @@
nodeLinker: node-modules

View File

@ -1,6 +1,6 @@
# create-svelte # sv
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
## Creating a project ## Creating a project

14
components.json Normal file
View File

@ -0,0 +1,14 @@
{
"$schema": "https://shadcn-svelte.com/schema.json",
"style": "default",
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app.css",
"baseColor": "slate"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils"
},
"typescript": true
}

View File

@ -12,20 +12,34 @@
"lint": "prettier --check ." "lint": "prettier --check ."
}, },
"devDependencies": { "devDependencies": {
"@melt-ui/pp": "^0.3.2",
"@melt-ui/svelte": "^0.85.0",
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0", "@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0",
"@tailwindcss/typography": "^0.5.15", "@types/dompurify": "^3",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"bits-ui": "^0.21.16",
"clsx": "^2.1.1",
"lucide-svelte": "^0.454.0",
"prettier": "^3.3.2", "prettier": "^3.3.2",
"prettier-plugin-svelte": "^3.2.6", "prettier-plugin-svelte": "^3.2.6",
"prettier-plugin-tailwindcss": "^0.6.5", "prettier-plugin-tailwindcss": "^0.6.5",
"svelte": "^5.0.0", "svelte": "^5.0.0",
"svelte-check": "^4.0.0", "svelte-check": "^4.0.0",
"tailwind-merge": "^2.5.4",
"tailwind-variants": "^0.2.1",
"tailwindcss": "^3.4.9", "tailwindcss": "^3.4.9",
"typescript": "^5.0.0", "typescript": "^5.0.0",
"vite": "^5.0.3" "vite": "^5.0.3"
},
"packageManager": "yarn@4.2.2+sha224.1e50daf19e5e249a025569752c60b88005fddf57d10fcde5fc68b88f",
"dependencies": {
"@iconify-json/mingcute": "^1.2.1",
"dompurify": "^3.1.7",
"highlight.js": "^11.10.0",
"isomorphic-dompurify": "^2.16.0",
"marked": "^14.1.3",
"marked-highlight": "^2.2.0",
"typescript-svelte-plugin": "^0.3.42",
"unplugin-icons": "^0.20.0"
} }
} }

View File

@ -1,16 +1,96 @@
@import 'tailwindcss/base'; @tailwind base;
@import 'tailwindcss/components'; @tailwind components;
@import 'tailwindcss/utilities'; @tailwind utilities;
@import url('https://fonts.googleapis.com/css2?family=LXGW+WenKai+TC&display=swap'); @layer base {
:root {
--background: 0 0% 100%;
--foreground: 224 71.4% 4.1%;
body{ --muted: 220 14.3% 95.9%;
display:block; --muted-foreground: 220 8.9% 46.1%;
margin:0px;
width:100dvw; --popover: 0 0% 100%;
min-height: 100dvh; --popover-foreground: 224 71.4% 4.1%;
font-family: "LXGW WenKai TC", cursive;
font-weight: 400; --card: 0 0% 100%;
font-style: normal; --card-foreground: 224 71.4% 4.1%;
overflow-x: hidden;
} --border: 220 13% 91%;
--input: 220 13% 91%;
--primary: 33deg 43% 91%;
--primary-foreground: 210 20% 98%;
--secondary: 220 14.3% 95.9%;
--secondary-foreground: 220.9 39.3% 11%;
--accent: 220 14.3% 95.9%;
--accent-foreground: 220.9 39.3% 11%;
--destructive: 0 72.2% 50.6%;
--destructive-foreground: 210 20% 98%;
--ring: 224 71.4% 4.1%;
--radius: 0.5rem;
}
.dark {
--background: 249 22% 12%;
--foreground: 245 50% 91%;
--muted: 215 27.9% 16.9%;
--muted-foreground: 217.9 10.6% 64.9%;
--popover: 224 71.4% 4.1%;
--popover-foreground: 210 20% 98%;
--card: 35 88% 72%;
--card-foreground: 248 13% 36%;
--border: 215 27.9% 16.9%;
--input: 215 27.9% 16.9%;
--primary: 248deg 25% 18%;
--primary-foreground: 248deg 15% 61%;
--secondary: 249deg 15% 28%;
--secondary-foreground: 210 20% 98%;
--accent: 215 27.9% 16.9%;
--accent-foreground: 210 20% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 20% 98%;
--ring: 216 12.2% 83.9%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
@media (prefers-color-scheme: dark) {
body {
@apply dark;
}
}
@font-face {
font-family: "GenRyuMin2TW";
src:
url("/GenRyuMin2TW-M.woff2") format("woff2");
}
body {
font-family: "GenRyuMin2TW";
overflow-x: hidden;
}

3
src/app.d.ts vendored
View File

@ -1,5 +1,6 @@
// See https://svelte.dev/docs/kit/types#app.d.ts // See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces // for information about these interfaces
import 'unplugin-icons/types/svelte'
declare global { declare global {
namespace App { namespace App {
// interface Error {} // interface Error {}
@ -10,4 +11,4 @@ declare global {
} }
} }
export {}; export { };

View File

@ -7,8 +7,6 @@
%sveltekit.head% %sveltekit.head%
</head> </head>
<body data-sveltekit-preload-data="hover"> <body data-sveltekit-preload-data="hover">
<div style="display: contents"> <div style="display: contents">%sveltekit.body%</div>
%sveltekit.body%
</div>
</body> </body>
</html> </html>

View File

@ -0,0 +1,36 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
import X from "lucide-svelte/icons/x";
import * as Dialog from "./index.js";
import { cn, flyAndScale } from "$lib/utils.js";
type $$Props = DialogPrimitive.ContentProps;
let className: $$Props["class"] = undefined;
export let transition: $$Props["transition"] = flyAndScale;
export let transitionConfig: $$Props["transitionConfig"] = {
duration: 200,
};
export { className as class };
</script>
<Dialog.Portal>
<Dialog.Overlay />
<DialogPrimitive.Content
{transition}
{transitionConfig}
class={cn(
"bg-background fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg sm:rounded-lg md:w-full",
className
)}
{...$$restProps}
>
<slot />
<DialogPrimitive.Close
class="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none"
>
<X class="h-4 w-4" />
<span class="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</Dialog.Portal>

View File

@ -0,0 +1,16 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
type $$Props = DialogPrimitive.DescriptionProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<DialogPrimitive.Description
class={cn("text-muted-foreground text-sm", className)}
{...$$restProps}
>
<slot />
</DialogPrimitive.Description>

View File

@ -0,0 +1,16 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<div
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
{...$$restProps}
>
<slot />
</div>

View File

@ -0,0 +1,13 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<div class={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)} {...$$restProps}>
<slot />
</div>

View File

@ -0,0 +1,21 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
import { fade } from "svelte/transition";
import { cn } from "$lib/utils.js";
type $$Props = DialogPrimitive.OverlayProps;
let className: $$Props["class"] = undefined;
export let transition: $$Props["transition"] = fade;
export let transitionConfig: $$Props["transitionConfig"] = {
duration: 150,
};
export { className as class };
</script>
<DialogPrimitive.Overlay
{transition}
{transitionConfig}
class={cn("bg-background/80 fixed inset-0 z-50 backdrop-blur-sm", className)}
{...$$restProps}
/>

View File

@ -0,0 +1,8 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
type $$Props = DialogPrimitive.PortalProps;
</script>
<DialogPrimitive.Portal {...$$restProps}>
<slot />
</DialogPrimitive.Portal>

View File

@ -0,0 +1,16 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
type $$Props = DialogPrimitive.TitleProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<DialogPrimitive.Title
class={cn("text-lg font-semibold leading-none tracking-tight", className)}
{...$$restProps}
>
<slot />
</DialogPrimitive.Title>

View File

@ -0,0 +1,37 @@
import { Dialog as DialogPrimitive } from "bits-ui";
import Title from "./dialog-title.svelte";
import Portal from "./dialog-portal.svelte";
import Footer from "./dialog-footer.svelte";
import Header from "./dialog-header.svelte";
import Overlay from "./dialog-overlay.svelte";
import Content from "./dialog-content.svelte";
import Description from "./dialog-description.svelte";
const Root = DialogPrimitive.Root;
const Trigger = DialogPrimitive.Trigger;
const Close = DialogPrimitive.Close;
export {
Root,
Title,
Portal,
Footer,
Header,
Trigger,
Overlay,
Content,
Description,
Close,
//
Root as Dialog,
Title as DialogTitle,
Portal as DialogPortal,
Footer as DialogFooter,
Header as DialogHeader,
Trigger as DialogTrigger,
Overlay as DialogOverlay,
Content as DialogContent,
Description as DialogDescription,
Close as DialogClose,
};

View File

@ -0,0 +1,28 @@
import Root from "./textarea.svelte";
type FormTextareaEvent<T extends Event = Event> = T & {
currentTarget: EventTarget & HTMLTextAreaElement;
};
type TextareaEvents = {
blur: FormTextareaEvent<FocusEvent>;
change: FormTextareaEvent<Event>;
click: FormTextareaEvent<MouseEvent>;
focus: FormTextareaEvent<FocusEvent>;
keydown: FormTextareaEvent<KeyboardEvent>;
keypress: FormTextareaEvent<KeyboardEvent>;
keyup: FormTextareaEvent<KeyboardEvent>;
mouseover: FormTextareaEvent<MouseEvent>;
mouseenter: FormTextareaEvent<MouseEvent>;
mouseleave: FormTextareaEvent<MouseEvent>;
paste: FormTextareaEvent<ClipboardEvent>;
input: FormTextareaEvent<InputEvent>;
};
export {
Root,
//
Root as Textarea,
type TextareaEvents,
type FormTextareaEvent,
};

View File

@ -0,0 +1,38 @@
<script lang="ts">
import type { HTMLTextareaAttributes } from "svelte/elements";
import type { TextareaEvents } from "./index.js";
import { cn } from "$lib/utils.js";
type $$Props = HTMLTextareaAttributes;
type $$Events = TextareaEvents;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"] = undefined;
export { className as class };
// Workaround for https://github.com/sveltejs/svelte/issues/9305
// Fixed in Svelte 5, but not backported to 4.x.
export let readonly: $$Props["readonly"] = undefined;
</script>
<textarea
class={cn(
"border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex min-h-[80px] w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
bind:value
{readonly}
on:blur
on:change
on:click
on:focus
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input
{...$$restProps}
></textarea>

567
src/lib/hljs/rose-pine.css Normal file
View File

@ -0,0 +1,567 @@
* {
--color-rosepine-base: #191724;
--color-rosepine-surface: #1f1d2e;
--color-rosepine-overlay: #26233a;
--color-rosepine-muted: #6e6a86;
--color-rosepine-subtle: #908caa;
--color-rosepine-text: #e0def4;
--color-rosepine-love: #eb6f92;
--color-rosepine-gold: #f6c177;
--color-rosepine-rose: #ebbcba;
--color-rosepine-pine: #31748f;
--color-rosepine-foam: #9ccfd8;
--color-rosepine-iris: #c4a7e7;
--color-rosepine-highlight-low: #21202e;
--color-rosepine-highlight-med: #403d52;
--color-rosepine-highlight-high: #524f67;
}
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em
}
code.hljs {
padding: 3px 5px;
}
.hljs {
background: var(--color-rosepine-overlay);
}
.hljs,
.hljs-subst {
color: var(--color-rosepine-text);
}
.hljs-selector-tag {
color: var(--color-rosepine-foam);
}
.hljs-selector-id {
color: var(--color-rosepine-iris);
font-weight: 700
}
.hljs-selector-attr,
.hljs-selector-class {
color: var(--color-rosepine-iris)
}
.hljs-property,
.hljs-selector-pseudo {
color: var(--color-rosepine-rose);
}
.hljs-addition {
background-color: rgba(156, 207, 216, .5)
}
.hljs-deletion {
background-color: rgba(235, 111, 146, .5)
}
.hljs-built_in,
.hljs-class,
.hljs-type {
color: var(--color-rosepine-iris);
}
.hljs-function,
.hljs-function>.hljs-title,
.hljs-title.hljs-function {
color: var(--color-rosepine-rose);
}
.hljs-keyword,
.hljs-literal,
.hljs-symbol {
color: var(--color-rosepine-foam);
}
.hljs-number {
color: var(--color-rosepine-gold);
}
.hljs-regexp {
color: var(--color-rosepine-gold);
}
.hljs-string {
color: var(--color-rosepine-foam);
}
.hljs-title {
color: var(--color-rosepine-iris)
}
.hljs-params {
color: var(--color-rosepine-text);
}
.hljs-bullet {
color: var(--color-rosepine-foam);
}
.hljs-code {
color: var(--color-rosepine-iris);
;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-formula {
color: var(--color-rosepine-iris);
}
.hljs-strong {
font-weight: 700;
}
.hljs-link:hover {
text-decoration: underline;
}
.hljs-comment,
.hljs-quote {
color: var(--color-rosepine-muted);
}
.hljs-doctag {
color: var(--color-rosepine-iris);
}
.hljs-meta,
.hljs-meta .hljs-keyword {
color: var(--color-rosepine-pine);
}
.hljs-meta .hljs-string {
color: var(--color-rosepine-foam);
}
.hljs-attr {
color: var(--color-rosepine-iris);
}
.hljs-attribute {
color: var(--color-rosepine-text);
}
.hljs-name {
color: var(--color-rosepine-foam);
}
.hljs-section {
color: var(--color-rosepine-rose);
}
.hljs-tag {
color: var(--color-rosepine-foam);
}
.hljs-template-variable,
.hljs-variable {
color: var(--color-rosepine-text);
}
.hljs-template-tag {
color: var(--color-rosepine-pine);
}
.language-abnf .hljs-attribute {
color: var(--color-rosepine-rose);
}
.language-abnf .hljs-symbol {
color: var(--color-rosepine-gold);
}
.language-apache .hljs-attribute {
color: var(--color-rosepine-rose);
}
.language-apache .hljs-section {
color: var(--color-rosepine-foam);
}
.language-arduino .hljs-built_in {
color: var(--color-rosepine-rose);
}
.language-aspectj .hljs-meta {
color: var(--color-rosepine-love);
}
.language-aspectj>.hljs-title {
color: var(--color-rosepine-rose);
}
.language-bnf .hljs-attribute {
color: var(--color-rosepine-iris)
}
.language-clojure .hljs-name {
color: var(--color-rosepine-rose);
}
.language-clojure .hljs-symbol {
color: var(--color-rosepine-gold);
}
.language-coq .hljs-built_in {
color: var(--color-rosepine-rose);
}
.language-cpp .hljs-meta .hljs-string {
color: var(--color-rosepine-iris);
}
.language-css .hljs-built_in {
color: var(--color-rosepine-rose);
}
.language-css .hljs-keyword {
color: var(--color-rosepine-love);
}
.language-diff .hljs-meta,
.language-ebnf .hljs-attribute {
color: var(--color-rosepine-iris);
}
.language-glsl .hljs-built_in {
color: var(--color-rosepine-rose);
}
.language-groovy .hljs-meta:not(:first-child),
.language-haxe .hljs-meta,
.language-java .hljs-meta {
color: var(--color-rosepine-love);
}
.language-ldif .hljs-attribute {
color: var(--color-rosepine-iris);
}
.language-lisp .hljs-name,
.language-lua .hljs-built_in,
.language-moonscript .hljs-built_in,
.language-nginx .hljs-attribute {
color: var(--color-rosepine-rose);
}
.language-nginx .hljs-section {
color: var(--color-rosepine-pine);
}
.language-pf .hljs-built_in,
.language-processing .hljs-built_in {
color: var(--color-rosepine-rose);
}
.language-scss .hljs-keyword,
.language-stylus .hljs-keyword {
color: var(--color-rosepine-foam);
}
.language-swift .hljs-meta {
color: var(--color-rosepine-love);
}
.language-vim .hljs-built_in {
color: var(--color-rosepine-rose);
font-style: italic
}
.language-yaml .hljs-meta {
color: var(--color-rosepine-love);
}
@media (prefers-color-scheme: light) {
* {
--color-rosepine-base: #faf4ed;
--color-rosepine-surface: #fffaf3;
--color-rosepine-overlay: #f2e9e1;
--color-rosepine-muted: #9893a5;
--color-rosepine-subtle: #797593;
--color-rosepine-text: #575279;
--color-rosepine-love: #b4637a;
--color-rosepine-gold: #ea9d34;
--color-rosepine-rose: #d7827e;
--color-rosepine-pine: #286983;
--color-rosepine-foam: #56949f;
--color-rosepine-iris: #907aa9;
--color-rosepine-highlight-low: #f4ede8;
--color-rosepine-highlight-med: #dfdad9;
--color-rosepine-highlight-high: #cecacd;
}
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em;
}
code.hljs {
padding: 3px 5px;
}
.hljs {
background: var(--color-rosepine-overlay);
}
.hljs,
.hljs-subst {
color: var(--color-rosepine-text);
}
.hljs-selector-tag {
color: var(--color-rosepine-foam);
}
.hljs-selector-id {
color: var(--color-rosepine-iris);
font-weight: 700
}
.hljs-selector-attr,
.hljs-selector-class {
color: var(--color-rosepine-iris)
}
.hljs-property,
.hljs-selector-pseudo {
color: var(--color-rosepine-rose);
}
.hljs-addition {
background-color: rgba(156, 207, 216, 0.5)
}
.hljs-deletion {
background-color: rgba(235, 111, 146, .5)
}
.hljs-built_in,
.hljs-class,
.hljs-type {
color: var(--color-rosepine-iris);
}
.hljs-function,
.hljs-function>.hljs-title,
.hljs-title.hljs-function {
color: var(--color-rosepine-rose);
}
.hljs-keyword,
.hljs-literal,
.hljs-symbol {
color: var(--color-rosepine-foam);
}
.hljs-number {
color: var(--color-rosepine-gold);
}
.hljs-regexp {
color: var(--color-rosepine-gold);
}
.hljs-string {
color: var(--color-rosepine-foam);
}
.hljs-title {
color: var(--color-rosepine-iris)
}
.hljs-params {
color: var(--color-rosepine-text);
}
.hljs-bullet {
color: var(--color-rosepine-foam);
}
.hljs-code {
color: var(--color-rosepine-iris);
}
.hljs-emphasis {
font-style: italic;
}
.hljs-formula {
color: var(--color-rosepine-iris);
}
.hljs-strong {
font-weight: 700;
}
.hljs-link:hover {
text-decoration: underline;
}
.hljs-comment,
.hljs-quote {
color: var(--color-rosepine-muted);
}
.hljs-doctag {
color: var(--color-rosepine-iris);
}
.hljs-meta,
.hljs-meta .hljs-keyword {
color: var(--color-rosepine-pine);
}
.hljs-meta .hljs-string {
color: var(--color-rosepine-foam);
}
.hljs-attr {
color: var(--color-rosepine-iris);
}
.hljs-attribute {
color: var(--color-rosepine-text);
}
.hljs-name {
color: var(--color-rosepine-foam);
}
.hljs-section {
color: var(--color-rosepine-rose);
}
.hljs-tag {
color: var(--color-rosepine-foam);
}
.hljs-template-variable,
.hljs-variable {
color: var(--color-rosepine-text);
}
.hljs-template-tag {
color: var(--color-rosepine-pine);
}
.language-abnf .hljs-attribute {
color: var(--color-rosepine-rose);
}
.language-abnf .hljs-symbol {
color: var(--color-rosepine-gold);
}
.language-apache .hljs-attribute {
color: var(--color-rosepine-rose);
}
.language-apache .hljs-section {
color: var(--color-rosepine-foam);
}
.language-arduino .hljs-built_in {
color: var(--color-rosepine-rose);
}
.language-aspectj .hljs-meta {
color: var(--color-rosepine-love);
}
.language-aspectj>.hljs-title {
color: var(--color-rosepine-rose);
}
.language-bnf .hljs-attribute {
color: var(--color-rosepine-iris)
}
.language-clojure .hljs-name {
color: var(--color-rosepine-rose);
}
.language-clojure .hljs-symbol {
color: var(--color-rosepine-gold);
}
.language-coq .hljs-built_in {
color: var(--color-rosepine-rose);
}
.language-cpp .hljs-meta .hljs-string {
color: var(--color-rosepine-iris);
}
.language-css .hljs-built_in {
color: var(--color-rosepine-rose);
}
.language-css .hljs-keyword {
color: var(--color-rosepine-love);
}
.language-diff .hljs-meta,
.language-ebnf .hljs-attribute {
color: var(--color-rosepine-iris);
}
.language-glsl .hljs-built_in {
color: var(--color-rosepine-rose);
}
.language-groovy .hljs-meta:not(:first-child),
.language-haxe .hljs-meta,
.language-java .hljs-meta {
color: var(--color-rosepine-love);
}
.language-ldif .hljs-attribute {
color: var(--color-rosepine-iris);
}
.language-lisp .hljs-name,
.language-lua .hljs-built_in,
.language-moonscript .hljs-built_in,
.language-nginx .hljs-attribute {
color: var(--color-rosepine-rose);
}
.language-nginx .hljs-section {
color: var(--color-rosepine-pine);
}
.language-pf .hljs-built_in,
.language-processing .hljs-built_in {
color: var(--color-rosepine-rose);
}
.language-scss .hljs-keyword,
.language-stylus .hljs-keyword {
color: var(--color-rosepine-foam);
}
.language-swift .hljs-meta {
color: var(--color-rosepine-love);
}
.language-vim .hljs-built_in {
color: var(--color-rosepine-rose);
font-style: italic
}
.language-yaml .hljs-meta {
color: var(--color-rosepine-love);
}
}

62
src/lib/utils.ts Normal file
View File

@ -0,0 +1,62 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import { cubicOut } from "svelte/easing";
import type { TransitionConfig } from "svelte/transition";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
type FlyAndScaleParams = {
y?: number;
x?: number;
start?: number;
duration?: number;
};
export const flyAndScale = (
node: Element,
params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
): TransitionConfig => {
const style = getComputedStyle(node);
const transform = style.transform === "none" ? "" : style.transform;
const scaleConversion = (
valueA: number,
scaleA: [number, number],
scaleB: [number, number]
) => {
const [minA, maxA] = scaleA;
const [minB, maxB] = scaleB;
const percentage = (valueA - minA) / (maxA - minA);
const valueB = percentage * (maxB - minB) + minB;
return valueB;
};
const styleToString = (
style: Record<string, number | string | undefined>
): string => {
return Object.keys(style).reduce((str, key) => {
if (style[key] === undefined) return str;
return str + `${key}:${style[key]};`;
}, "");
};
return {
duration: params.duration ?? 200,
delay: 0,
css: (t) => {
const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
return styleToString({
transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
opacity: t
});
},
easing: cubicOut
};
};

View File

@ -1,20 +1,16 @@
<script lang="ts"> <script lang="ts">
import "../app.css" import '../app.css';
let { children } = $props(); let { children } = $props();
let template1 = ["grid-cols-4 h-16", "col-span-3 text-4xl grid-cols-4", ""]
let template2 = ["grid-cols-1 grid-rows-4 h-1/4", "place-items-center row-span-3 text-6xl text-center", ""]
let template = $state(template1)
</script> </script>
<div class="grid w-full {template[0]} sticky top-0"> <div class="sticky top-0 z-10 grid h-16 w-full grid-cols-4 bg-background">
<div class="grid {template[1]}"> <div class="col-span-3 grid grid-cols-4 text-4xl">
<a class="m-auto" href="/">中工匿名</a> <a class="m-auto" href="/">中工匿名</a>
</div> </div>
<div class="grid grid-cols-3 grid-rows-1 text-center {template[2]}"> <div class="grid grid-cols-3 grid-rows-1 text-center">
<a class="my-auto" href="/tos">服務條款</a> <a class="my-auto" href="/tos">服務條款</a>
<a class="my-auto" href="/links">鏈接</a> <a class="my-auto" href="/links">鏈接</a>
<a class="my-auto" href="/fav">每月精選</a> <a class="my-auto" href="/fav">每月精選</a>
</div> </div>
</div> </div>
{@render children()} {@render children()}

View File

@ -1,43 +1,49 @@
<script lang="ts"> <script lang="ts">
let body_state = $state(false); import MessageCard from './message_card.svelte';
$inspect(body_state) import MingcuteAddCircleFill from '~icons/mingcute/add-circle-fill';
const overlay = () => { import Overlay, { toggle_overlay, get_overlay } from './overlay.svelte';
body_state = !body_state; import DOMPurify from 'isomorphic-dompurify';
if(body_state){ import Marker from './marker.svelte';
document.body.classList.add("overflow-y-hidden");
}else{ let value: string;
document.body.classList.remove("overflow-y-hidden");
} const overlay = () => {
}; if (!get_overlay()) {
} else {
}
toggle_overlay();
};
</script> </script>
<div style="width:100dvw;" class="h-dvh grid">
<p class="justify-self-center m-auto">空空如也...</p> <div style="width:100dvw;" class="grid h-dvh px-5 py-3">
</div> <!-- <p class="justify-self-center m-auto">空空如也...</p> -->
<button id="add" onclick={overlay}> <MessageCard
+ content="
</button> # H1
<div id="overlay" class:overlay_enable={body_state} class:hidden={!body_state}> ## H2
<div> ### H3
<button onclick={overlay}>close</button> [url](https://google.com)
</div> ![image](http://localhost:5173/favicon.png)
Test
NormalText
"
/>
</div> </div>
<button id="add" onclick={overlay} class="text-3xl"> <MingcuteAddCircleFill /> </button>
<Overlay>
<div class="flex w-4/6 flex-col rounded-lg bg-primary px-5 py-3" style="height:60dvh;">
<div class="relative h-6 w-full">
<button class="absolute left-0" onclick={overlay}>取消</button>
<button class="absolute right-0">發送</button>
</div>
<Marker text={value} height="h-[calc(100%-1.5rem)]" />
</div>
</Overlay>
<style> <style>
#add{ #add {
position: fixed; position: fixed;
bottom:calc(15dvh); bottom: calc(15dvh);
right:calc(10dvw); right: calc(10dvw);
} }
#overlay{ </style>
position: fixed;
width:100%;
height: 100%;
top:0;
left:0;
z-index: 10;
cursor:pointer;
}
.overlay_enable{
backdrop-filter: blur(3px);
}
</style>

92
src/routes/marker.svelte Normal file
View File

@ -0,0 +1,92 @@
<script lang="ts">
let { text, height }: { text: string; height: string } = $props();
import { Textarea } from '$lib/components/ui/textarea';
import { Marked } from 'marked';
import DOMPurify from 'isomorphic-dompurify';
import type { KeyboardEventHandler } from 'svelte/elements';
import { markedHighlight } from 'marked-highlight';
import hljs from 'highlight.js';
import '$lib/hljs/rose-pine.css';
let window_size = $state(0);
let value = $state('');
const key_scrap: KeyboardEventHandler<HTMLTextAreaElement> = (event: KeyboardEvent) => {
let ct: HTMLTextAreaElement = event.currentTarget as HTMLTextAreaElement;
if (event.key == 'Tab') {
event.preventDefault();
let start = ct.selectionStart;
let end = ct.selectionEnd;
ct.value = ct.value.substring(0, start) + '\t' + ct.value.substring(end);
ct.selectionStart = ct.selectionEnd = start + 1;
}
};
// marked
const marked = new Marked(
markedHighlight({
emptyLangClass: 'hljs',
langPrefix: 'hljs language-',
highlight(code, lang, info) {
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
return hljs.highlight(code, { language }).value;
}
})
);
</script>
<svelte:window bind:innerWidth={window_size} />
<div class="{height} w-full">
<div class="h-[4%] w-full"></div>
{#if window_size < 360}
<div>
<Textarea class="h-[96%] w-full resize-none" />
</div>
{:else}
<div class="grid h-[96%] w-full grid-cols-2 gap-5">
<textarea
tabindex="0"
class="resize-none rounded-lg bg-background p-1 text-foreground focus:outline-none focus:ring focus:ring-primary-foreground"
bind:value
onkeydown={key_scrap}
></textarea>
<div class="inner_card">
{@html marked.parse(value).toString()}
</div>
</div>
{/if}
</div>
<style>
.inner_card :global {
max-width: 100%;
overflow-y: scroll;
a {
@apply text-cyan-500;
}
p:has(code) {
overflow-x: scroll;
padding: 10px 0px;
code {
/* @apply bg-amber-600; */
text-wrap: nowrap;
}
}
p {
word-break: keep-all;
overflow-wrap: break-word;
}
pre {
/* @apply bg-amber-600; */
overflow-x: scroll;
}
h1 {
font-size: 1.3rem;
}
h2 {
font-size: 1.2rem;
}
h3 {
font-size: 1.1rem;
}
}
</style>

View File

@ -0,0 +1,29 @@
<script lang="ts">
let { content }: { content: string } = $props();
import { marked } from 'marked';
import * as Dialog from '$lib/components/ui/dialog';
//TODO Need sanitizer
</script>
<div
class="card_eff flex max-h-80 max-w-64 flex-col overflow-y-hidden bg-amber-400 px-5 font-bold text-slate-900"
>
<div class="inner_card">
{@html marked(content)}
</div>
</div>
<style>
.card_eff {
filter: drop-shadow(5px 10px #f38f0f);
}
.inner_card :global {
a {
@apply text-cyan-500;
}
code {
@apply bg-amber-600;
}
mask-image: linear-gradient(0deg, transparent, white 50%);
}
</style>

32
src/routes/overlay.svelte Normal file
View File

@ -0,0 +1,32 @@
<script module lang="ts">
let overlay = $state(false);
const toggle_overlay = () => {
overlay = !overlay;
if (overlay) {
document.body.classList.add('overflow-y-hidden');
} else {
document.body.classList.remove('overflow-y-hidden');
}
};
const get_overlay = () => {
return overlay;
};
export { toggle_overlay, get_overlay };
</script>
<script lang="ts">
let { children } = $props();
</script>
<div
class={'fixed left-0 top-0 z-20 h-dvh w-dvw ' +
(overlay ? 'overlay_blur grid place-items-center' : 'hidden')}
>
{@render children()}
</div>
<style>
.overlay_blur {
backdrop-filter: blur(10px);
}
</style>

View File

@ -1,5 +1,6 @@
<div class="flex justify-center"> <div class="flex justify-center">
<p class="w-1/2 break-keep text-2xl"> <div class="w-1/2 break-keep text-center text-2xl">
“中工匿名”不記錄個人網路位置, 名字。可選用匿名,或佚名作為發表時署名。如訊息侵害他人權利,違反大眾道德觀,皆不保證留存。 <p>“中工匿名”不記錄個人網路位置、名字。</p>
</p> <p>如訊息侵害他人權利,違反大眾道德觀,皆不保證留存。</p>
</div> </div>
</div>

3
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
/// <reference types="svelte" />
/// <reference types="vite/client" />
/// <reference types="unplugin-icons/types/svelte" />

BIN
static/GenRyuMin2TW-M.woff2 Normal file

Binary file not shown.

View File

@ -1,15 +1,11 @@
import adapter from '@sveltejs/adapter-auto'; import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import { preprocessMeltUI, sequence } from '@melt-ui/pp'
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */
const config = { const config = {
// Consult https://svelte.dev/docs/kit/integrations // Consult https://svelte.dev/docs/kit/integrations
// for more information about preprocessors // for more information about preprocessors
preprocess: sequence([ preprocess: vitePreprocess(),
vitePreprocess(),
preprocessMeltUI()
]),
kit: { kit: {
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.

View File

@ -1,12 +1,64 @@
import typography from '@tailwindcss/typography'; import { fontFamily } from "tailwindcss/defaultTheme";
import type { Config } from 'tailwindcss'; import type { Config } from "tailwindcss";
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
const config: Config = {
darkMode: ["class"],
content: ["./src/**/*.{html,js,svelte,ts}"],
safelist: ["dark"],
theme: { theme: {
extend: {} container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px"
}
},
extend: {
colors: {
border: "hsl(var(--border) / <alpha-value>)",
input: "hsl(var(--input) / <alpha-value>)",
ring: "hsl(var(--ring) / <alpha-value>)",
background: "hsl(var(--background) / <alpha-value>)",
foreground: "hsl(var(--foreground) / <alpha-value>)",
primary: {
DEFAULT: "hsl(var(--primary) / <alpha-value>)",
foreground: "hsl(var(--primary-foreground) / <alpha-value>)"
},
secondary: {
DEFAULT: "hsl(var(--secondary) / <alpha-value>)",
foreground: "hsl(var(--secondary-foreground) / <alpha-value>)"
},
destructive: {
DEFAULT: "hsl(var(--destructive) / <alpha-value>)",
foreground: "hsl(var(--destructive-foreground) / <alpha-value>)"
},
muted: {
DEFAULT: "hsl(var(--muted) / <alpha-value>)",
foreground: "hsl(var(--muted-foreground) / <alpha-value>)"
},
accent: {
DEFAULT: "hsl(var(--accent) / <alpha-value>)",
foreground: "hsl(var(--accent-foreground) / <alpha-value>)"
},
popover: {
DEFAULT: "hsl(var(--popover) / <alpha-value>)",
foreground: "hsl(var(--popover-foreground) / <alpha-value>)"
},
card: {
DEFAULT: "hsl(var(--card) / <alpha-value>)",
foreground: "hsl(var(--card-foreground) / <alpha-value>)"
}
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)"
},
fontFamily: {
sans: [...fontFamily.sans]
}
}
}, },
};
plugins: [typography] export default config;
} as Config;

View File

@ -9,7 +9,15 @@
"skipLibCheck": true, "skipLibCheck": true,
"sourceMap": true, "sourceMap": true,
"strict": true, "strict": true,
"moduleResolution": "bundler" "moduleResolution": "bundler",
"plugins": [
{
"name": "typescript-svelte-plugin",
// the following options can be set additionally; they are optional; their default values are listed here
"enabled": true, // enables this plugin
"assumeIsSvelteProject": false // if true, skip detection and always assume it's a Svelte project
}
]
} }
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files

View File

@ -1,6 +1,7 @@
import { sveltekit } from '@sveltejs/kit/vite'; import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import Icons from 'unplugin-icons/vite'
export default defineConfig({ export default defineConfig({
plugins: [sveltekit()] plugins: [sveltekit(), Icons({ compiler: 'svelte' })]
}); });

3316
yarn.lock Normal file

File diff suppressed because it is too large Load Diff