diff --git a/Dockerfile b/Dockerfile
index 0f03ddd..40477ee 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -5,7 +5,7 @@ RUN apk add nodejs npm
WORKDIR /source
COPY . .
-WORKDIR /source/pkg/view
+WORKDIR /source/pkg/views
RUN npm install
RUN npm run build
WORKDIR /source
@@ -18,4 +18,4 @@ COPY --from=interactive-server /dist /interactive/server
EXPOSE 8445
-CMD ["/interactive/server"]
\ No newline at end of file
+CMD ["/interactive/server"]
diff --git a/pkg/server/startup.go b/pkg/server/startup.go
index 0360151..1fdb923 100644
--- a/pkg/server/startup.go
+++ b/pkg/server/startup.go
@@ -5,7 +5,7 @@ import (
"strings"
"time"
- "code.smartsheep.studio/hydrogen/interactive/pkg/view"
+ "code.smartsheep.studio/hydrogen/identity/pkg/views"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cache"
"github.com/gofiber/fiber/v2/middleware/cors"
@@ -99,7 +99,7 @@ func NewServer() {
Expiration: 24 * time.Hour,
CacheControl: true,
}), filesystem.New(filesystem.Config{
- Root: http.FS(view.FS),
+ Root: http.FS(views.FS),
PathPrefix: "dist",
Index: "index.html",
NotFoundFile: "dist/index.html",
diff --git a/pkg/view/.gitignore b/pkg/view/.gitignore
deleted file mode 100644
index 886c090..0000000
--- a/pkg/view/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-/dist
-/node_modules
-
-.DS_Store
-
-package-lock.json
-yarn.lock
diff --git a/pkg/view/README.md b/pkg/view/README.md
deleted file mode 100644
index 99613fc..0000000
--- a/pkg/view/README.md
+++ /dev/null
@@ -1,28 +0,0 @@
-## Usage
-
-```bash
-$ npm install # or pnpm install or yarn install
-```
-
-### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
-
-## Available Scripts
-
-In the project directory, you can run:
-
-### `npm run dev`
-
-Runs the app in the development mode.
-Open [http://localhost:5173](http://localhost:5173) to view it in the browser.
-
-### `npm run build`
-
-Builds the app for production to the `dist` folder.
-It correctly bundles Solid in production mode and optimizes the build for the best performance.
-
-The build is minified and the filenames include the hashes.
-Your app is ready to be deployed!
-
-## Deployment
-
-Learn more about deploying your application with the [documentations](https://vitejs.dev/guide/static-deploy.html)
diff --git a/pkg/view/embed.go b/pkg/view/embed.go
deleted file mode 100644
index ec34587..0000000
--- a/pkg/view/embed.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package view
-
-import "embed"
-
-//go:embed all:dist
-var FS embed.FS
diff --git a/pkg/view/embed.html b/pkg/view/embed.html
deleted file mode 100644
index 18963b3..0000000
--- a/pkg/view/embed.html
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
- Embedded Interactive
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/pkg/view/index.html b/pkg/view/index.html
deleted file mode 100644
index 731eaea..0000000
--- a/pkg/view/index.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
- Goatplaza
-
-
-
-
-
-
diff --git a/pkg/view/package.json b/pkg/view/package.json
deleted file mode 100644
index 112e107..0000000
--- a/pkg/view/package.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- "name": "@hydrogen/interactive-web",
- "private": true,
- "version": "0.0.0",
- "type": "module",
- "scripts": {
- "dev": "vite",
- "build": "tsc && vite build",
- "preview": "vite preview"
- },
- "dependencies": {
- "@fortawesome/fontawesome-free": "^6.5.1",
- "@solidjs/router": "^0.10.10",
- "artplayer": "^5.1.1",
- "cherry-markdown": "^0.8.38",
- "dompurify": "^3.0.8",
- "flv.js": "^1.6.2",
- "hls.js": "^1.5.3",
- "marked": "^12.0.0",
- "medium-zoom": "^1.1.0",
- "solid-js": "^1.8.7",
- "universal-cookie": "^7.0.2"
- },
- "devDependencies": {
- "@tailwindcss/typography": "^0.5.10",
- "@types/dompurify": "^3.0.5",
- "autoprefixer": "^10.4.17",
- "daisyui": "^4.6.1",
- "postcss": "^8.4.33",
- "solid-devtools": "^0.29.3",
- "tailwindcss": "^3.4.1",
- "typescript": "^5.2.2",
- "vite": "^5.0.8",
- "vite-plugin-solid": "^2.8.0"
- }
-}
diff --git a/pkg/view/postcss.config.js b/pkg/view/postcss.config.js
deleted file mode 100644
index 2e7af2b..0000000
--- a/pkg/view/postcss.config.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export default {
- plugins: {
- tailwindcss: {},
- autoprefixer: {},
- },
-}
diff --git a/pkg/view/public/favicon.svg b/pkg/view/public/favicon.svg
deleted file mode 100644
index 3edea9a..0000000
--- a/pkg/view/public/favicon.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
\ No newline at end of file
diff --git a/pkg/view/src/.prettierrc b/pkg/view/src/.prettierrc
deleted file mode 100644
index 78bd232..0000000
--- a/pkg/view/src/.prettierrc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "printWidth": 120,
- "tabWidth": 2,
- "singleQuote": false
-}
\ No newline at end of file
diff --git a/pkg/view/src/assets/fonts/fonts.css b/pkg/view/src/assets/fonts/fonts.css
deleted file mode 100644
index 3688303..0000000
--- a/pkg/view/src/assets/fonts/fonts.css
+++ /dev/null
@@ -1,184 +0,0 @@
-:root {
- --bs-body-font-family: "IBM Plex Sans", "Noto Serif SC", sans-serif !important;
-}
-
-html,
-body {
- font-family: var(--bs-body-font-family);
-}
-
-/* ibm-plex-sans-100 - latin */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: 'IBM Plex Sans';
- font-style: normal;
- font-weight: 100;
- src: url('./ibm-plex-sans-v19-latin-100.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-/* ibm-plex-sans-100italic - latin */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: 'IBM Plex Sans';
- font-style: italic;
- font-weight: 100;
- src: url('./ibm-plex-sans-v19-latin-100italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-/* ibm-plex-sans-200 - latin */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: 'IBM Plex Sans';
- font-style: normal;
- font-weight: 200;
- src: url('./ibm-plex-sans-v19-latin-200.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-/* ibm-plex-sans-200italic - latin */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: 'IBM Plex Sans';
- font-style: italic;
- font-weight: 200;
- src: url('./ibm-plex-sans-v19-latin-200italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-/* ibm-plex-sans-300 - latin */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: 'IBM Plex Sans';
- font-style: normal;
- font-weight: 300;
- src: url('./ibm-plex-sans-v19-latin-300.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-/* ibm-plex-sans-300italic - latin */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: 'IBM Plex Sans';
- font-style: italic;
- font-weight: 300;
- src: url('./ibm-plex-sans-v19-latin-300italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-/* ibm-plex-sans-regular - latin */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: 'IBM Plex Sans';
- font-style: normal;
- font-weight: 400;
- src: url('./ibm-plex-sans-v19-latin-regular.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-/* ibm-plex-sans-italic - latin */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: 'IBM Plex Sans';
- font-style: italic;
- font-weight: 400;
- src: url('./ibm-plex-sans-v19-latin-italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-/* ibm-plex-sans-500 - latin */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: 'IBM Plex Sans';
- font-style: normal;
- font-weight: 500;
- src: url('./ibm-plex-sans-v19-latin-500.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-/* ibm-plex-sans-500italic - latin */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: 'IBM Plex Sans';
- font-style: italic;
- font-weight: 500;
- src: url('./ibm-plex-sans-v19-latin-500italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-/* ibm-plex-sans-600 - latin */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: 'IBM Plex Sans';
- font-style: normal;
- font-weight: 600;
- src: url('./ibm-plex-sans-v19-latin-600.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-/* ibm-plex-sans-600italic - latin */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: 'IBM Plex Sans';
- font-style: italic;
- font-weight: 600;
- src: url('./ibm-plex-sans-v19-latin-600italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-/* ibm-plex-sans-700 - latin */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: 'IBM Plex Sans';
- font-style: normal;
- font-weight: 700;
- src: url('./ibm-plex-sans-v19-latin-700.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-/* ibm-plex-sans-700italic - latin */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: 'IBM Plex Sans';
- font-style: italic;
- font-weight: 700;
- src: url('./ibm-plex-sans-v19-latin-700italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-
-/* noto-serif-sc-200 - chinese-simplified */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: "Noto Serif SC";
- font-style: normal;
- font-weight: 200;
- src: url("./noto-serif-sc-v22-chinese-simplified-200.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-
-/* noto-serif-sc-300 - chinese-simplified */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: "Noto Serif SC";
- font-style: normal;
- font-weight: 300;
- src: url("./noto-serif-sc-v22-chinese-simplified-300.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-
-/* noto-serif-sc-regular - chinese-simplified */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: "Noto Serif SC";
- font-style: normal;
- font-weight: 400;
- src: url("./noto-serif-sc-v22-chinese-simplified-regular.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-
-/* noto-serif-sc-500 - chinese-simplified */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: "Noto Serif SC";
- font-style: normal;
- font-weight: 500;
- src: url("./noto-serif-sc-v22-chinese-simplified-500.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-
-/* noto-serif-sc-600 - chinese-simplified */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: "Noto Serif SC";
- font-style: normal;
- font-weight: 600;
- src: url("./noto-serif-sc-v22-chinese-simplified-600.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-
-/* noto-serif-sc-700 - chinese-simplified */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: "Noto Serif SC";
- font-style: normal;
- font-weight: 700;
- src: url("./noto-serif-sc-v22-chinese-simplified-700.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
-
-/* noto-serif-sc-900 - chinese-simplified */
-@font-face {
- font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
- font-family: "Noto Serif SC";
- font-style: normal;
- font-weight: 900;
- src: url("./noto-serif-sc-v22-chinese-simplified-900.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
-}
diff --git a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-100.woff2 b/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-100.woff2
deleted file mode 100755
index bbe5a8b..0000000
Binary files a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-100.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-100italic.woff2 b/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-100italic.woff2
deleted file mode 100755
index 075d1c8..0000000
Binary files a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-100italic.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-200.woff2 b/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-200.woff2
deleted file mode 100755
index 13e9069..0000000
Binary files a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-200.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-200italic.woff2 b/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-200italic.woff2
deleted file mode 100755
index 0947013..0000000
Binary files a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-200italic.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-300.woff2 b/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-300.woff2
deleted file mode 100755
index 8737b6b..0000000
Binary files a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-300.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-300italic.woff2 b/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-300italic.woff2
deleted file mode 100755
index a5f9f4c..0000000
Binary files a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-300italic.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-500.woff2 b/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-500.woff2
deleted file mode 100755
index adbbd4c..0000000
Binary files a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-500.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-500italic.woff2 b/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-500italic.woff2
deleted file mode 100755
index 6d6a9b1..0000000
Binary files a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-500italic.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-600.woff2 b/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-600.woff2
deleted file mode 100755
index 0ac91d6..0000000
Binary files a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-600.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-600italic.woff2 b/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-600italic.woff2
deleted file mode 100755
index c71f94c..0000000
Binary files a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-600italic.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-700.woff2 b/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-700.woff2
deleted file mode 100755
index da7d57f..0000000
Binary files a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-700.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-700italic.woff2 b/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-700italic.woff2
deleted file mode 100755
index 9cd1838..0000000
Binary files a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-700italic.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-italic.woff2 b/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-italic.woff2
deleted file mode 100755
index eea1618..0000000
Binary files a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-italic.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-regular.woff2 b/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-regular.woff2
deleted file mode 100755
index 93bcd64..0000000
Binary files a/pkg/view/src/assets/fonts/ibm-plex-sans-v19-latin-regular.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-200.woff2 b/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-200.woff2
deleted file mode 100755
index d90173a..0000000
Binary files a/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-200.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-300.woff2 b/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-300.woff2
deleted file mode 100755
index 385e4f8..0000000
Binary files a/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-300.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-500.woff2 b/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-500.woff2
deleted file mode 100755
index 9699234..0000000
Binary files a/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-500.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-600.woff2 b/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-600.woff2
deleted file mode 100755
index ee418fd..0000000
Binary files a/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-600.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-700.woff2 b/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-700.woff2
deleted file mode 100755
index 03494a8..0000000
Binary files a/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-700.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-900.woff2 b/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-900.woff2
deleted file mode 100755
index 149b461..0000000
Binary files a/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-900.woff2 and /dev/null differ
diff --git a/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-regular.woff2 b/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-regular.woff2
deleted file mode 100755
index 6c638af..0000000
Binary files a/pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-regular.woff2 and /dev/null differ
diff --git a/pkg/view/src/components/Avatar.tsx b/pkg/view/src/components/Avatar.tsx
deleted file mode 100644
index 4912ced..0000000
--- a/pkg/view/src/components/Avatar.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Show } from "solid-js";
-
-export default function Avatar(props: { user: any }) {
- return (
-
-
- {props.user?.name?.substring(0, 1)}
-
-
- }
- >
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/pkg/view/src/components/LoadingAnimation.tsx b/pkg/view/src/components/LoadingAnimation.tsx
deleted file mode 100644
index ea2dd8f..0000000
--- a/pkg/view/src/components/LoadingAnimation.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-export default function LoadingAnimation() {
- return (
-
-
-
Listening to the latest news...
-
- )
-}
\ No newline at end of file
diff --git a/pkg/view/src/components/NameCard.module.css b/pkg/view/src/components/NameCard.module.css
deleted file mode 100644
index 932298a..0000000
--- a/pkg/view/src/components/NameCard.module.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.description {
- color: var(--fallback-bc, oklch(var(--bc)/.8));
-}
\ No newline at end of file
diff --git a/pkg/view/src/components/NameCard.tsx b/pkg/view/src/components/NameCard.tsx
deleted file mode 100644
index f1e870a..0000000
--- a/pkg/view/src/components/NameCard.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { createSignal, Show } from "solid-js";
-
-import styles from "./NameCard.module.css";
-import { getAtk } from "../stores/userinfo.tsx";
-import { request } from "../scripts/request.ts";
-
-export default function NameCard(props: { accountId: string, onError: (messasge: string | null) => void }) {
- const [info, setInfo] = createSignal(null);
- const [isFollowing, setIsFollowing] = createSignal(false);
-
- const [_, setLoading] = createSignal(true);
- const [submitting, setSubmitting] = createSignal(false);
-
- async function readInfo() {
- setLoading(true);
- const res = await request(`/api/users/${props.accountId}`);
- if (res.status !== 200) {
- props.onError(await res.text());
- } else {
- setInfo(await res.json());
- props.onError(null);
- }
- setLoading(false);
- }
-
- async function readIsFollowing() {
- setLoading(true);
- const res = await request(`/api/users/${props.accountId}/follow`, {
- method: "GET",
- headers: { Authorization: `Bearer ${getAtk()}` }
- });
- if (res.status === 200) {
- const data = await res.json();
- setIsFollowing(data["is_followed"]);
- }
- setLoading(false);
- }
-
- async function follow() {
- setSubmitting(true);
- const res = await request(`/api/users/${props.accountId}/follow`, {
- method: "POST",
- headers: { "Authorization": `Bearer ${getAtk()}` }
- });
- if (res.status !== 201 && res.status !== 204) {
- props.onError(await res.text());
- } else {
- await readIsFollowing();
- props.onError(null);
- }
- setSubmitting(false);
- }
-
- readInfo();
- readIsFollowing();
-
- return (
-
-
-
-
-
-
-
-
-
-
-
- follow()}>
-
- Follow
-
- }>
-
-
-
-
-
-
-
{info()?.name}
-
{info()?.description}
-
-
-
- Joined at {new Date(info()?.created_at).toLocaleString()}
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/pkg/view/src/components/posts/PostAttachments.module.css b/pkg/view/src/components/posts/PostAttachments.module.css
deleted file mode 100644
index 368ec7e..0000000
--- a/pkg/view/src/components/posts/PostAttachments.module.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.attachmentsControl {
- background-color: transparent !important;
-}
\ No newline at end of file
diff --git a/pkg/view/src/components/posts/PostAttachments.tsx b/pkg/view/src/components/posts/PostAttachments.tsx
deleted file mode 100644
index 7d95d54..0000000
--- a/pkg/view/src/components/posts/PostAttachments.tsx
+++ /dev/null
@@ -1,158 +0,0 @@
-import { createEffect, createMemo, createSignal, Match, Switch } from "solid-js";
-import mediumZoom from "medium-zoom";
-
-import styles from "./PostAttachments.module.css";
-
-import Artplayer from "artplayer";
-import HlsJs from "hls.js";
-import FlvJs from "flv.js";
-
-function Video({ url, ...rest }: any) {
- let container: any;
-
- function playM3u8(video: HTMLVideoElement, url: string, art: Artplayer) {
- if (HlsJs.isSupported()) {
- if (art.hls) art.hls.destroy();
- const hls = new HlsJs();
- hls.loadSource(url);
- hls.attachMedia(video);
- art.hls = hls;
- art.on("destroy", () => hls.destroy());
- } else if (video.canPlayType("application/vnd.apple.mpegurl")) {
- video.src = url;
- } else {
- art.notice.show = "Unsupported playback format: m3u8";
- }
- }
-
- function playFlv(video: HTMLVideoElement, url: string, art: Artplayer) {
- if (FlvJs.isSupported()) {
- if (art.flv) art.flv.destroy();
- const flv = FlvJs.createPlayer({ type: "flv", url });
- flv.attachMediaElement(video);
- flv.load();
- art.flv = flv;
- art.on("destroy", () => flv.destroy());
- } else {
- art.notice.show = "Unsupported playback format: flv";
- }
- }
-
- createEffect(() => {
- new Artplayer({
- container: container as HTMLDivElement,
- url: url,
- setting: true,
- flip: true,
- loop: true,
- playbackRate: true,
- aspectRatio: true,
- subtitleOffset: true,
- fullscreen: true,
- fullscreenWeb: true,
- theme: "#49509e",
- customType: {
- m3u8: playM3u8,
- flv: playFlv
- }
- });
- });
-
- return (
-
- );
-}
-
-function Audio({ url, caption, ...rest }: any) {
-
- return (
-
- );
-}
-
-
-export default function PostAttachments(props: { attachments: any[] }) {
- if (props.attachments.length <= 0) return null;
-
- const [focus, setFocus] = createSignal(0);
- const item = createMemo(() => props.attachments[focus()]);
-
- function getRenderType(item: any): string {
- return item.mimetype.split("/")[0];
- }
-
- function getUrl(item: any): string {
- return item.external_url ? item.external_url : `/api/attachments/o/${item.file_id}`;
- }
-
- createEffect(() => {
- mediumZoom(document.querySelectorAll(".attachment-image img"), {
- background: "var(--fallback-b1,oklch(var(--b1)/1))"
- });
- }, [focus()]);
-
- return (
- <>
-
-
- Attached {props.attachments.length} file{props.attachments.length > 1 ? "s" : null}
-
-
-
-
-
-
{item().filename}
-
-
-
{item().filesize <= 0 ? "Unknown" : item().filesize} Bytes
-
{item().mimetype}
-
-
-
-
-
- }>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- File {focus() + 1}
-
-
-
-
- >
- );
-}
\ No newline at end of file
diff --git a/pkg/view/src/components/posts/PostEditActions.tsx b/pkg/view/src/components/posts/PostEditActions.tsx
deleted file mode 100644
index bd3008c..0000000
--- a/pkg/view/src/components/posts/PostEditActions.tsx
+++ /dev/null
@@ -1,399 +0,0 @@
-import { closeModel, openModel } from "../../scripts/modals.ts";
-import { createSignal, For, Match, Show, Switch } from "solid-js";
-import { getAtk, useUserinfo } from "../../stores/userinfo.tsx";
-import { request } from "../../scripts/request.ts";
-
-import styles from "./PostPublish.module.css";
-
-export default function PostEditActions(props: {
- editing?: any;
- onInputAlias: (value: string) => void;
- onInputPublish: (value: string) => void;
- onInputAttachments: (value: any[]) => void;
- onInputCategories: (categories: any[]) => void;
- onInputTags: (tags: any[]) => void;
- onError: (message: string | null) => void;
-}) {
- const userinfo = useUserinfo();
-
- const [uploading, setUploading] = createSignal(false);
-
- const [attachments, setAttachments] = createSignal(props.editing?.attachments ?? []);
- const [categories, setCategories] = createSignal<{ alias: string; name: string }[]>(props.editing?.categories ?? []);
- const [tags, setTags] = createSignal<{ alias: string; name: string }[]>(props.editing?.tags ?? []);
-
- const [availableCategories, setAvailableCategories] = createSignal([]);
- const [attachmentMode, setAttachmentMode] = createSignal(0);
-
- async function readCategories() {
- const res = await request("/api/categories");
- if (res.status === 200) {
- setAvailableCategories(await res.json());
- }
- }
-
- readCategories();
-
- async function uploadAttachment(evt: SubmitEvent) {
- evt.preventDefault();
-
- const form = evt.target as HTMLFormElement;
- const data = new FormData(form);
- if (!data.get("attachment")) return;
-
- setUploading(true);
- const res = await request("/api/attachments", {
- method: "POST",
- headers: { Authorization: `Bearer ${getAtk()}` },
- body: data,
- });
- if (res.status !== 200) {
- props.onError(await res.text());
- } else {
- const data = await res.json();
- setAttachments(attachments().concat([data.info]));
- props.onInputAttachments(attachments());
- props.onError(null);
- form.reset();
- }
- setUploading(false);
- }
-
- function addAttachment(evt: SubmitEvent) {
- evt.preventDefault();
-
- const form = evt.target as HTMLFormElement;
- const data = Object.fromEntries(new FormData(form));
-
- setAttachments(
- attachments().concat([
- {
- ...data,
- author_id: userinfo?.profiles?.id,
- },
- ]),
- );
- props.onInputAttachments(attachments());
- form.reset();
- }
-
- function removeAttachment(idx: number) {
- const data = attachments().slice();
- data.splice(idx, 1);
- setAttachments(data);
- props.onInputAttachments(attachments());
- }
-
- function addCategory(evt: SubmitEvent) {
- evt.preventDefault();
-
- const form = evt.target as HTMLFormElement;
- const data = Object.fromEntries(new FormData(form));
- if (!data.category) return;
-
- const item = availableCategories().find((item) => item.alias === data.category);
-
- setCategories(categories().concat([item]));
- props.onInputCategories(categories());
- form.reset();
- }
-
- function removeCategory(idx: number) {
- const data = categories().slice();
- data.splice(idx, 1);
- setCategories(data);
- props.onInputCategories(categories());
- }
-
- function addTag(evt: SubmitEvent) {
- evt.preventDefault();
-
- const form = evt.target as HTMLFormElement;
- const data = Object.fromEntries(new FormData(evt.target as HTMLFormElement));
- if (!data.alias) data.alias = crypto.randomUUID().replace(/-/g, "");
- if (!data.name) return;
-
- setTags(tags().concat([data as any]));
- props.onInputTags(tags());
- form.reset();
- }
-
- function removeTag(idx: number) {
- const data = tags().slice();
- data.splice(idx, 1);
- setTags(data);
- props.onInputTags(tags());
- }
-
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/pkg/view/src/components/posts/PostEditor.tsx b/pkg/view/src/components/posts/PostEditor.tsx
deleted file mode 100644
index 61736ca..0000000
--- a/pkg/view/src/components/posts/PostEditor.tsx
+++ /dev/null
@@ -1,223 +0,0 @@
-import { createEffect, createMemo, createSignal, For, onMount, Show } from "solid-js";
-
-import Cherry from "cherry-markdown";
-import "cherry-markdown/dist/cherry-markdown.min.css";
-import { getAtk } from "../../stores/userinfo.tsx";
-import { request } from "../../scripts/request.ts";
-import PostEditActions from "./PostEditActions.tsx";
-
-export default function PostEditor(props: {
- editing?: any,
- onError: (message: string | null) => void,
- onPost: () => void
-}) {
- let editorContainer: any;
- const [editor, setEditor] = createSignal();
- const [realmList, setRealmList] = createSignal([]);
-
- const [submitting, setSubmitting] = createSignal(false);
-
- const [alias, setAlias] = createSignal("");
- const [publishedAt, setPublishedAt] = createSignal("");
- const [attachments, setAttachments] = createSignal([]);
- const [categories, setCategories] = createSignal<{ alias: string, name: string }[]>([]);
- const [tags, setTags] = createSignal<{ alias: string, name: string }[]>([]);
-
- const theme = createMemo(() => {
- if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
- return "dark";
- } else {
- return "light";
- }
- });
-
- createEffect(() => {
- editor()?.setTheme(theme());
- }, [editor(), theme()]);
-
- onMount(() => {
- if (editorContainer) {
- setEditor(new Cherry({
- el: editorContainer,
- value: "Welcome to the creator hub! " +
- "We provide a better editor than normal mode for you! " +
- "So you can tell us your mind clearly. " +
- "Delete this paragraph and getting start!"
- }));
- }
- });
-
- createEffect(() => {
- setAttachments(props.editing?.attachments ?? []);
- setCategories(props.editing?.categories ?? []);
- setTags(props.editing?.tags ?? []);
- editor()?.setValue(props.editing?.content);
- }, [props.editing]);
-
- async function listRealm() {
- const res = await request("/api/realms/me/available", {
- headers: { "Authorization": `Bearer ${getAtk()}` }
- });
- if (res.status === 200) {
- setRealmList(await res.json());
- }
- }
-
- listRealm();
-
- async function doPost(evt: SubmitEvent) {
- evt.preventDefault();
-
- const form = evt.target as HTMLFormElement;
- const data = Object.fromEntries(new FormData(form));
- if (!editor()?.getValue()) return;
-
- setSubmitting(true);
- const res = await request("/api/posts", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- "Authorization": `Bearer ${getAtk()}`
- },
- body: JSON.stringify({
- alias: alias() ? alias() : crypto.randomUUID().replace(/-/g, ""),
- title: data.title,
- content: editor()?.getValue(),
- attachments: attachments(),
- categories: categories(),
- tags: tags(),
- realm_id: parseInt(data.realm as string) !== 0 ? parseInt(data.realm as string) : undefined,
- published_at: publishedAt() ? new Date(publishedAt()) : new Date()
- })
- });
- if (res.status !== 200) {
- props.onError(await res.text());
- } else {
- form.reset();
- props.onError(null);
- props.onPost();
- }
- setSubmitting(false);
- }
-
- async function doEdit(evt: SubmitEvent) {
- evt.preventDefault();
-
- const form = evt.target as HTMLFormElement;
- const data = Object.fromEntries(new FormData(form));
- if (!editor()?.getValue()) return;
-
- setSubmitting(true);
- const res = await request(`/api/posts/${props.editing?.id}`, {
- method: "PUT",
- headers: {
- "Content-Type": "application/json",
- "Authorization": `Bearer ${getAtk()}`
- },
- body: JSON.stringify({
- alias: alias() ? alias() : crypto.randomUUID().replace(/-/g, ""),
- title: data.title,
- content: editor()?.getValue(),
- attachments: attachments(),
- categories: categories(),
- tags: tags(),
- published_at: publishedAt() ? new Date(publishedAt()) : new Date()
- })
- });
- if (res.status !== 200) {
- props.onError(await res.text());
- } else {
- form.reset();
- props.onError(null);
- props.onPost();
- }
- setSubmitting(false);
- }
-
- function resetForm() {
- setAttachments([]);
- setCategories([]);
- setTags([]);
- }
-
- return (
-
- );
-}
\ No newline at end of file
diff --git a/pkg/view/src/components/posts/PostItem.tsx b/pkg/view/src/components/posts/PostItem.tsx
deleted file mode 100644
index f20057c..0000000
--- a/pkg/view/src/components/posts/PostItem.tsx
+++ /dev/null
@@ -1,215 +0,0 @@
-import { createSignal, For, Show } from "solid-js";
-import { getAtk, useUserinfo } from "../../stores/userinfo.tsx";
-import { request } from "../../scripts/request.ts";
-import PostAttachments from "./PostAttachments.tsx";
-import * as marked from "marked";
-import DOMPurify from "dompurify";
-import Avatar from "../Avatar.tsx";
-
-export default function PostItem(props: {
- post: any;
- noClick?: boolean;
- noAuthor?: boolean;
- noControl?: boolean;
- noRelated?: boolean;
- noContent?: boolean;
- onRepost?: (post: any) => void;
- onReply?: (post: any) => void;
- onEdit?: (post: any) => void;
- onDelete?: (post: any) => void;
- onSearch?: (filter: any) => void;
- onError: (message: string | null) => void;
- onReact: () => void;
-}) {
- const [reacting, setReacting] = createSignal(false);
-
- const userinfo = useUserinfo();
-
- async function reactPost(item: any, type: string) {
- setReacting(true);
- const res = await request(`/api/posts/${item.id}/react/${type}`, {
- method: "POST",
- headers: { Authorization: `Bearer ${getAtk()}` }
- });
- if (res.status !== 201 && res.status !== 204) {
- props.onError(await res.text());
- } else {
- props.onReact();
- props.onError(null);
- }
- setReacting(false);
- }
-
- const content = ;
-
- return (
-
-
-
-
-
-
-
-
{props.post.author.nick}
-
{props.post.author.description}
-
-
-
-
-
-
-
-
-
{props.post.title}
-
- {content}
-
-
-
-
-
0}>
-
-
-
-
-
-
- Reposted a post
-
-
-
-
-
-
- Replied a post
-
-
-
-
-
-
-
-
-
-
- Login! To access entire platform.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/pkg/view/src/components/posts/PostList.module.css b/pkg/view/src/components/posts/PostList.module.css
deleted file mode 100644
index 49d4448..0000000
--- a/pkg/view/src/components/posts/PostList.module.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.paginationControl {
- background-color: transparent !important;
-}
\ No newline at end of file
diff --git a/pkg/view/src/components/posts/PostList.tsx b/pkg/view/src/components/posts/PostList.tsx
deleted file mode 100644
index f8d814c..0000000
--- a/pkg/view/src/components/posts/PostList.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import { createMemo, createSignal, For, Show } from "solid-js";
-
-import styles from "./PostList.module.css";
-import PostItem from "./PostItem.tsx";
-import LoadingAnimation from "../LoadingAnimation.tsx";
-import { getAtk } from "../../stores/userinfo.tsx";
-import { request } from "../../scripts/request.ts";
-
-export default function PostList(props: {
- noRelated?: boolean,
- info: { data: any[], count: number } | null,
- onRepost?: (post: any) => void,
- onReply?: (post: any) => void,
- onEdit?: (post: any) => void,
- onUpdate: (pn: number, filter?: any) => Promise,
- onError: (message: string | null) => void
-}) {
- const [loading, setLoading] = createSignal(true);
-
- const posts = createMemo(() => props.info?.data);
- const postCount = createMemo(() => props.info?.count ?? 0);
-
- const [page, setPage] = createSignal(1);
- const pageCount = createMemo(() => Math.ceil(postCount() / 10));
-
- async function readPosts(filter?: any) {
- setLoading(true);
- await props.onUpdate(page(), filter);
- setLoading(false);
- }
-
- readPosts();
-
- async function deletePost(item: any) {
- if (!confirm(`Are you sure to delete post#${item.id}?`)) return;
-
- setLoading(true);
- const res = await request(`/api/posts/${item.id}`, {
- method: "DELETE",
- headers: { "Authorization": `Bearer ${getAtk()}` }
- });
- if (res.status !== 200) {
- props.onError(await res.text());
- } else {
- await readPosts();
- props.onError(null);
- }
- setLoading(false);
- }
-
- function changePage(pn: number) {
- setPage(pn);
- readPosts().then(() => {
- setTimeout(() => window.scrollTo({ top: 0, behavior: "smooth" }), 16);
- });
- }
-
- return (
-
-
-
- {item =>
- readPosts()}
- onError={props.onError}
- />
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/pkg/view/src/components/posts/PostPublish.module.css b/pkg/view/src/components/posts/PostPublish.module.css
deleted file mode 100644
index 1c05ece..0000000
--- a/pkg/view/src/components/posts/PostPublish.module.css
+++ /dev/null
@@ -1,8 +0,0 @@
-.publishInput {
- outline-style: none !important;
- outline-width: 0 !important;
-}
-
-.description {
- color: var(--fallback-bc, oklch(var(--bc)/.8));
-}
\ No newline at end of file
diff --git a/pkg/view/src/components/posts/PostPublish.tsx b/pkg/view/src/components/posts/PostPublish.tsx
deleted file mode 100644
index 41994a0..0000000
--- a/pkg/view/src/components/posts/PostPublish.tsx
+++ /dev/null
@@ -1,210 +0,0 @@
-import { createEffect, createSignal, Show } from "solid-js";
-import { getAtk, useUserinfo } from "../../stores/userinfo.tsx";
-import { request } from "../../scripts/request.ts";
-
-import styles from "./PostPublish.module.css";
-import PostEditActions from "./PostEditActions.tsx";
-import Avatar from "../Avatar.tsx";
-
-export default function PostPublish(props: {
- replying?: any,
- reposting?: any,
- editing?: any,
- realmId?: number,
- onReset: () => void,
- onError: (message: string | null) => void,
- onPost: () => void
-}) {
- const userinfo = useUserinfo();
-
- if (!userinfo?.isLoggedIn) {
- return (
-
-
-
Login!
-
Or keep silent.
-
-
- );
- }
-
- const [submitting, setSubmitting] = createSignal(false);
-
- const [alias, setAlias] = createSignal("");
- const [publishedAt, setPublishedAt] = createSignal("");
- const [attachments, setAttachments] = createSignal([]);
- const [categories, setCategories] = createSignal<{ alias: string, name: string }[]>([]);
- const [tags, setTags] = createSignal<{ alias: string, name: string }[]>([]);
-
- createEffect(() => {
- setAttachments(props.editing?.attachments ?? []);
- setCategories(props.editing?.categories ?? []);
- setTags(props.editing?.tags ?? []);
- }, [props.editing]);
-
- async function doPost(evt: SubmitEvent) {
- evt.preventDefault();
-
- const form = evt.target as HTMLFormElement;
- const data = Object.fromEntries(new FormData(form));
- if (!data.content) return;
-
- setSubmitting(true);
- const res = await request("/api/posts", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- "Authorization": `Bearer ${getAtk()}`
- },
- body: JSON.stringify({
- alias: alias() ? alias() : crypto.randomUUID().replace(/-/g, ""),
- title: data.title,
- content: data.content,
- attachments: attachments(),
- categories: categories(),
- tags: tags(),
- realm_id: data.publish_in_realm ? props.realmId : undefined,
- published_at: publishedAt() ? new Date(publishedAt()) : new Date(),
- repost_to: props.reposting?.id,
- reply_to: props.replying?.id
- })
- });
- if (res.status !== 200) {
- props.onError(await res.text());
- } else {
- form.reset();
- props.onError(null);
- props.onPost();
- }
- setSubmitting(false);
- }
-
- async function doEdit(evt: SubmitEvent) {
- evt.preventDefault();
-
- const form = evt.target as HTMLFormElement;
- const data = Object.fromEntries(new FormData(form));
- if (!data.content) return;
-
- setSubmitting(true);
- const res = await request(`/api/posts/${props.editing?.id}`, {
- method: "PUT",
- headers: {
- "Content-Type": "application/json",
- "Authorization": `Bearer ${getAtk()}`
- },
- body: JSON.stringify({
- alias: alias() ? alias() : crypto.randomUUID().replace(/-/g, ""),
- title: data.title,
- content: data.content,
- attachments: attachments(),
- categories: categories(),
- tags: tags(),
- realm_id: props.realmId,
- published_at: publishedAt() ? new Date(publishedAt()) : new Date()
- })
- });
- if (res.status !== 200) {
- props.onError(await res.text());
- } else {
- form.reset();
- props.onError(null);
- props.onPost();
- }
- setSubmitting(false);
- }
-
- function resetForm() {
- setAttachments([]);
- setCategories([]);
- setTags([]);
- props.onReset();
- }
-
- return (
- <>
-
- >
- );
-}
\ No newline at end of file
diff --git a/pkg/view/src/index.css b/pkg/view/src/index.css
deleted file mode 100644
index d91dbb3..0000000
--- a/pkg/view/src/index.css
+++ /dev/null
@@ -1,39 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-html, body {
- padding: 0;
- margin: 0;
-}
-
-.medium-zoom-image--opened {
- z-index: 15;
-}
-
-.medium-zoom-overlay {
- z-index: 10;
-}
-
-.scrollbar-hidden {
- scrollbar-width: none;
-}
-
-.scrollbar-hidden::-webkit-scrollbar {
- display: none;
- width: 0;
-}
-
-.cherry, .cherry-toolbar, .cherry-editor, .cherry-previewer, .cherry-drag {
- box-shadow: none !important;
-}
-
-.cherry-drag {
- width: 2px !important;
-}
-
-@media (prefers-color-scheme: dark) {
- .cherry-drag {
- background: oklch(var(--b2)) !important;
- }
-}
\ No newline at end of file
diff --git a/pkg/view/src/index.tsx b/pkg/view/src/index.tsx
deleted file mode 100644
index f54591f..0000000
--- a/pkg/view/src/index.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import "solid-devtools";
-
-/* @refresh reload */
-import { render } from "solid-js/web";
-
-import "./index.css";
-import "./assets/fonts/fonts.css";
-import { lazy } from "solid-js";
-import { Route, Router } from "@solidjs/router";
-
-import "@fortawesome/fontawesome-free/css/all.css";
-
-import RootLayout from "./layouts/RootLayout.tsx";
-import FeedView from "./pages/view.tsx";
-import Global from "./pages/global.tsx";
-import PostReference from "./pages/post.tsx";
-import CreatorView from "./pages/creators/view.tsx";
-import { UserinfoProvider } from "./stores/userinfo.tsx";
-import { WellKnownProvider } from "./stores/wellKnown.tsx";
-
-const root = document.getElementById("root");
-
-const router = (basename?: string) => (
-
-
-
-
-
-
- import("./pages/search.tsx"))} />
- import("./pages/realms"))} />
- import("./pages/realms/realm.tsx"))} />
- import("./pages/account.tsx"))} />
-
-
- import("./pages/creators"))} />
- import("./pages/creators/publish.tsx"))} />
- import("./pages/creators/edit.tsx"))} />
-
-
-
-
-);
-
-declare const __GARFISH_EXPORTS__: {
- provider: Object;
- registerProvider?: (provider: any) => void;
-};
-
-declare global {
- interface Window {
- __GARFISH__: boolean;
- __LAUNCHPAD_TARGET__?: string;
- }
-}
-
-export const provider = () => ({
- render: ({ dom, basename }: { dom: any, basename: string }) => {
- render(
- () => router(basename),
- dom.querySelector("#root")
- );
- },
- destroy: () => {
- }
-});
-
-if (!window.__GARFISH__) {
- console.log("Running directly!")
- render(router, root!);
-} else if (typeof __GARFISH_EXPORTS__ !== "undefined") {
- console.log("Running in launchpad container!")
- console.log("Launchpad target:", window.__LAUNCHPAD_TARGET__)
- if (__GARFISH_EXPORTS__.registerProvider) {
- __GARFISH_EXPORTS__.registerProvider(provider);
- } else {
- __GARFISH_EXPORTS__.provider = provider;
- }
-}
\ No newline at end of file
diff --git a/pkg/view/src/layouts/RootLayout.tsx b/pkg/view/src/layouts/RootLayout.tsx
deleted file mode 100644
index 0671270..0000000
--- a/pkg/view/src/layouts/RootLayout.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import Navigator from "./shared/Navigator.tsx";
-import { readProfiles, useUserinfo } from "../stores/userinfo.tsx";
-import { createEffect, createMemo, createSignal, Show } from "solid-js";
-import { readWellKnown } from "../stores/wellKnown.tsx";
-import { BeforeLeaveEventArgs, useLocation, useNavigate, useSearchParams } from "@solidjs/router";
-
-export default function RootLayout(props: any) {
- const [ready, setReady] = createSignal(false);
-
- Promise.all([readWellKnown(), readProfiles()]).then(() => setReady(true));
-
- const navigate = useNavigate();
- const userinfo = useUserinfo();
-
- const [searchParams] = useSearchParams();
- const location = useLocation();
-
- createEffect(() => {
- if (ready()) {
- keepGate(location.pathname + location.search, searchParams["embedded"] != null);
- }
- }, [ready, userinfo]);
-
- function keepGate(path: string, embedded: boolean, e?: BeforeLeaveEventArgs) {
- const blacklist = ["/creators"];
-
- if (!userinfo?.isLoggedIn && blacklist.includes(path)) {
- if (!e?.defaultPrevented) e?.preventDefault();
- if (embedded) {
- navigate(`/auth?redirect_uri=${path}&embedded=${location.query["embedded"]}`);
- } else {
- navigate(`/auth?redirect_uri=${path}`);
- }
- }
- }
-
- const mainContentStyles = createMemo(() => {
- if (!searchParams["embedded"]) {
- return "h-[calc(100vh-64px)] max-md:mb-[64px] md:mt-[64px]";
- } else {
- return "h-[100vh]";
- }
- });
-
- return (
-
-
-
-
-
- }
- >
-
-
-
-
- {props.children}
-
- );
-}
diff --git a/pkg/view/src/layouts/shared/Navigator.tsx b/pkg/view/src/layouts/shared/Navigator.tsx
deleted file mode 100644
index 0714c32..0000000
--- a/pkg/view/src/layouts/shared/Navigator.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import { createMemo, For, Match, Switch } from "solid-js";
-import { clearUserinfo, useUserinfo } from "../../stores/userinfo.tsx";
-import { useNavigate } from "@solidjs/router";
-import { useWellKnown } from "../../stores/wellKnown.tsx";
-
-interface MenuItem {
- icon: string;
- label: string;
- href?: string;
-}
-
-export default function Navigator() {
- const nav: MenuItem[] = [
- { icon: "fa-solid fa-pen-nib", label: "Creators", href: "/creators" },
- { icon: "fa-solid fa-newspaper", label: "Feed", href: "/" },
- { icon: "fa-solid fa-people-group", label: "Realms", href: "/realms" },
- ];
-
- const wellKnown = useWellKnown();
- const userinfo = useUserinfo();
- const navigate = useNavigate();
-
- const endpoint = createMemo(() => wellKnown?.components?.identity)
-
- function logout() {
- clearUserinfo();
- navigate("/");
- }
-
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
- Login
-
-
-
-
-
-
-
-
- {(item) => (
-
-
-
-
-
- )}
-
-
- >
- );
-}
diff --git a/pkg/view/src/pages/account.tsx b/pkg/view/src/pages/account.tsx
deleted file mode 100644
index 180ebb8..0000000
--- a/pkg/view/src/pages/account.tsx
+++ /dev/null
@@ -1,110 +0,0 @@
-import { createEffect, createSignal, Show } from "solid-js";
-import { useParams } from "@solidjs/router";
-import { useSearchParams } from "@solidjs/router";
-import { createStore } from "solid-js/store";
-import { closeModel, openModel } from "../scripts/modals.ts";
-import { request } from "../scripts/request.ts";
-
-import PostList from "../components/posts/PostList.tsx";
-import NameCard from "../components/NameCard.tsx";
-import PostPublish from "../components/posts/PostPublish.tsx";
-
-export default function AccountPage() {
- const [error, setError] = createSignal(null);
-
- const [page, setPage] = createSignal(0);
- const [info, setInfo] = createSignal(null);
-
- const [searchParams, setSearchParams] = useSearchParams();
- const params = useParams();
-
- createEffect(() => {
- setPage(parseInt(searchParams["page"] ?? "1"));
- }, [searchParams]);
-
- async function readPosts(pn?: number) {
- if (pn) setSearchParams({ page: pn });
- const res = await request(
- "/api/posts?" +
- new URLSearchParams({
- take: searchParams["take"] ? searchParams["take"] : (10).toString(),
- offset: searchParams["offset"] ? searchParams["offset"] : ((page() - 1) * 10).toString(),
- authorId: params["accountId"],
- }),
- );
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- setError(null);
- setInfo(await res.json());
- }
- }
-
- function setMeta(data: any, field: string, open = true) {
- const meta: { [id: string]: any } = {
- reposting: null,
- replying: null,
- editing: null,
- };
- meta[field] = data;
- setPublishMeta(meta);
-
- if (open) openModel("#post-publish");
- else closeModel("#post-publish");
- }
-
- const [publishMeta, setPublishMeta] = createStore({
- replying: null,
- reposting: null,
- editing: null,
- });
-
- return (
- <>
-
-
-
-
-
-
- setMeta(item, "reposting")}
- onReply={(item) => setMeta(item, "replying")}
- onEdit={(item) => setMeta(item, "editing")}
- />
- >
- );
-}
diff --git a/pkg/view/src/pages/creators/edit.tsx b/pkg/view/src/pages/creators/edit.tsx
deleted file mode 100644
index 1907f2e..0000000
--- a/pkg/view/src/pages/creators/edit.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import PostEdit from "../../components/posts/PostEditor.tsx";
-import { useNavigate, useParams } from "@solidjs/router";
-import { createSignal, Show } from "solid-js";
-import { getAtk } from "../../stores/userinfo.tsx";
-import { request } from "../../scripts/request.ts";
-
-export default function PublishPost() {
- const navigate = useNavigate();
- const params = useParams();
-
- const [error, setError] = createSignal(null);
- const [post, setPost] = createSignal();
-
- async function readPost() {
- const res = await request(`/api/creators/posts/${params["postId"]}`, {
- headers: { "Authorization": `Bearer ${getAtk()}` }
- });
- if (res.status === 200) {
- setPost((await res.json())["data"]);
- } else {
- setError(await res.text());
- }
- }
-
- readPost();
-
- return (
- <>
-
-
-
-
-
-
Edit「{post()?.title ? post()?.title : "Untitled"}」
-
-
-
-
-
- navigate("/creators")}
- />
- >
- );
-}
\ No newline at end of file
diff --git a/pkg/view/src/pages/creators/index.tsx b/pkg/view/src/pages/creators/index.tsx
deleted file mode 100644
index 19afe9a..0000000
--- a/pkg/view/src/pages/creators/index.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-import { createMemo, createSignal, For, Show } from "solid-js";
-import { getAtk } from "../../stores/userinfo.tsx";
-import LoadingAnimation from "../../components/LoadingAnimation.tsx";
-import styles from "../../components/posts/PostList.module.css";
-import { request } from "../../scripts/request.ts";
-
-export default function CreatorHub() {
- const [error, setError] = createSignal(null);
-
- const [posts, setPosts] = createSignal([]);
- const [postCount, setPostCount] = createSignal(0);
-
- const [page, setPage] = createSignal(1);
- const [loading, setLoading] = createSignal(false);
-
- const pageCount = createMemo(() => Math.ceil(postCount() / 10));
-
- async function readPosts(pn?: number) {
- if (pn) setPage(pn);
- setLoading(true);
- const res = await request("/api/creators/posts?" + new URLSearchParams({
- take: (10).toString(),
- offset: ((page() - 1) * 10).toString()
- }), { headers: { "Authorization": `Bearer ${getAtk()}` } });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- const data = await res.json();
- setError(null);
- setPosts(data["data"]);
- setPostCount(data["count"]);
- }
- setLoading(false);
- }
-
- readPosts();
-
- function changePage(pn: number) {
- readPosts(pn).then(() => {
- setTimeout(() => window.scrollTo({ top: 0, behavior: "smooth" }), 16);
- });
- }
-
- return (
- <>
-
-
-
-
-
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
\ No newline at end of file
diff --git a/pkg/view/src/pages/creators/publish.tsx b/pkg/view/src/pages/creators/publish.tsx
deleted file mode 100644
index 8a42931..0000000
--- a/pkg/view/src/pages/creators/publish.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import PostEdit from "../../components/posts/PostEditor.tsx";
-import { useNavigate } from "@solidjs/router";
-import { createSignal, Show } from "solid-js";
-
-export default function PublishPost() {
- const navigate = useNavigate();
-
- const [error, setError] = createSignal(null);
-
- return (
- <>
-
-
-
-
- navigate("/creators")}
- />
- >
- );
-}
\ No newline at end of file
diff --git a/pkg/view/src/pages/creators/view.module.css b/pkg/view/src/pages/creators/view.module.css
deleted file mode 100644
index b1e8a6d..0000000
--- a/pkg/view/src/pages/creators/view.module.css
+++ /dev/null
@@ -1,13 +0,0 @@
-.wrapper {
- display: grid;
- grid-template-columns: 1fr;
- column-gap: 20px;
-
- max-height: calc(100vh - 64px);
-}
-
-@media (min-width: 1024px) {
- .wrapper {
- grid-template-columns: 1fr 2fr;
- }
-}
\ No newline at end of file
diff --git a/pkg/view/src/pages/creators/view.tsx b/pkg/view/src/pages/creators/view.tsx
deleted file mode 100644
index 8645583..0000000
--- a/pkg/view/src/pages/creators/view.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { createMemo } from "solid-js";
-import { useSearchParams } from "@solidjs/router";
-
-import styles from "./view.module.css";
-
-export default function CreatorView(props: any) {
- const [searchParams] = useSearchParams();
-
- const scrollContentStyles = createMemo(() => {
- if (!searchParams["embedded"]) {
- return "max-md:mb-[64px]";
- } else {
- return "h-[100vh]";
- }
- });
-
- return (
-
-
-
Creator Hub
-
-
-
- {props.children}
-
-
- );
-}
diff --git a/pkg/view/src/pages/global.tsx b/pkg/view/src/pages/global.tsx
deleted file mode 100644
index 65d56e7..0000000
--- a/pkg/view/src/pages/global.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-import { createEffect, createSignal, Show } from "solid-js";
-import { createStore } from "solid-js/store";
-import { useSearchParams } from "@solidjs/router";
-import { request } from "../scripts/request.ts";
-
-import PostList from "../components/posts/PostList.tsx";
-import PostPublish from "../components/posts/PostPublish.tsx";
-
-export default function DashboardPage() {
- const [error, setError] = createSignal(null);
-
- const [page, setPage] = createSignal(0);
- const [info, setInfo] = createSignal(null);
-
- const [searchParams, setSearchParams] = useSearchParams();
-
- createEffect(() => {
- setPage(parseInt(searchParams["page"] ?? "1"));
- }, [searchParams]);
-
- async function readPosts(pn?: number) {
- if (pn) setSearchParams({ page: pn });
- const res = await request(
- "/api/posts?" +
- new URLSearchParams({
- take: searchParams["take"] ? searchParams["take"] : (10).toString(),
- offset: searchParams["offset"] ? searchParams["offset"] : ((page() - 1) * 10).toString(),
- reply: false.toString(),
- }),
- );
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- setError(null);
- setInfo(await res.json());
- }
- }
-
- function setMeta(data: any, field: string, scroll = true) {
- const meta: { [id: string]: any } = {
- reposting: null,
- replying: null,
- editing: null,
- };
- meta[field] = data;
- setPublishMeta(meta);
-
- if (scroll) window.scroll({ top: 0, behavior: "smooth" });
- }
-
- const [publishMeta, setPublishMeta] = createStore({
- replying: null,
- reposting: null,
- editing: null,
- });
-
- return (
- <>
-
-
- setMeta(null, "none", false)}
- onPost={() => readPosts()}
- onError={setError}
- />
-
- setMeta(item, "reposting")}
- onReply={(item) => setMeta(item, "replying")}
- onEdit={(item) => setMeta(item, "editing")}
- />
- >
- );
-}
diff --git a/pkg/view/src/pages/post.tsx b/pkg/view/src/pages/post.tsx
deleted file mode 100644
index f1b1b23..0000000
--- a/pkg/view/src/pages/post.tsx
+++ /dev/null
@@ -1,161 +0,0 @@
-import { createSignal, Show } from "solid-js";
-import { useNavigate, useParams, useSearchParams } from "@solidjs/router";
-import { createStore } from "solid-js/store";
-import { closeModel, openModel } from "../scripts/modals.ts";
-import { getAtk } from "../stores/userinfo.tsx";
-import { request } from "../scripts/request.ts";
-import PostPublish from "../components/posts/PostPublish.tsx";
-import PostList from "../components/posts/PostList.tsx";
-import PostItem from "../components/posts/PostItem.tsx";
-
-export default function PostPage() {
- const [error, setError] = createSignal(null);
-
- const [page, setPage] = createSignal(0);
- const [related, setRelated] = createSignal(null);
- const [info, setInfo] = createSignal(null);
-
- const params = useParams();
- const navigate = useNavigate();
-
- const [searchParams] = useSearchParams();
-
- async function readPost(pn?: number) {
- if (pn) setPage(pn);
- const res = await request(`/api/posts/${params["postId"]}?` + new URLSearchParams({
- take: (10).toString(),
- offset: ((page() - 1) * 10).toString()
- }));
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- setError(null);
- const data = await res.json();
- setInfo(data["data"]);
- setRelated({
- count: data["count"],
- data: data["related"]
- });
- }
- }
-
- readPost();
-
- async function deletePost(item: any) {
- if (!confirm(`Are you sure to delete post#${item.id}?`)) return;
-
- const res = await request(`/api/posts/${item.id}`, {
- method: "DELETE",
- headers: { "Authorization": `Bearer ${getAtk()}` }
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- back();
- setError(null);
- }
- }
-
- function setMeta(data: any, field: string, open = true) {
- const meta: { [id: string]: any } = {
- reposting: null,
- replying: null,
- editing: null
- };
- meta[field] = data;
- setPublishMeta(meta);
-
- if (open) openModel("#post-publish");
- else closeModel("#post-publish");
- }
-
- const [publishMeta, setPublishMeta] = createStore({
- replying: null,
- reposting: null,
- editing: null
- });
-
- function back() {
- if (window.history.length > 0) {
- window.history.back();
- } else {
- navigate("/");
- }
- }
-
- return (
- <>
-
-
-
-
back()}>
-
-
- }>
-
-
-
-
-
-
{searchParams["title"] ?? "Post details"}
-
-
-
-
-
-
-
- Creating fake news...
-
- }>
- setMeta(item, "reposting")}
- onReply={(item) => setMeta(item, "replying")}
- onEdit={(item) => setMeta(item, "editing")}
- />
-
- setMeta(item, "reposting")}
- onReply={(item) => setMeta(item, "replying")}
- onEdit={(item) => setMeta(item, "editing")}
- />
-
- >
- );
-}
\ No newline at end of file
diff --git a/pkg/view/src/pages/realms/index.tsx b/pkg/view/src/pages/realms/index.tsx
deleted file mode 100644
index f71a524..0000000
--- a/pkg/view/src/pages/realms/index.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import { createSignal, For, Show } from "solid-js";
-import { closeModel, openModel } from "../../scripts/modals.ts";
-import { getAtk } from "../../stores/userinfo.tsx";
-import { request } from "../../scripts/request.ts";
-
-export default function RealmDirectoryPage() {
- const [error, setError] = createSignal(null);
- const [submitting, setSubmitting] = createSignal(false);
-
- const [realms, setRealms] = createSignal(null);
-
- async function readRealms() {
- const res = await request(`/api/realms`);
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- setRealms(await res.json());
- }
- }
-
- readRealms();
-
- async function createRealm(evt: SubmitEvent) {
- evt.preventDefault();
-
- const form = evt.target as HTMLFormElement;
- const data = Object.fromEntries(new FormData(form));
-
- setSubmitting(true);
- const res = await request("/api/realms", {
- method: "POST",
- headers: { "Authorization": `Bearer ${getAtk()}`, "Content-Type": "application/json" },
- body: JSON.stringify({
- name: data.name,
- description: data.description,
- is_public: data.is_public != null
- })
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- await readRealms();
- closeModel("#create-realm");
- form.reset();
- }
- setSubmitting(false);
- }
-
- return (
- <>
-
-
-
-
Realms directory
-
-
-
-
- {item =>
-
{item.name}
-
{item.description}
-
-
-
}
-
-
-
- >
- );
-}
\ No newline at end of file
diff --git a/pkg/view/src/pages/realms/realm.module.css b/pkg/view/src/pages/realms/realm.module.css
deleted file mode 100644
index 932298a..0000000
--- a/pkg/view/src/pages/realms/realm.module.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.description {
- color: var(--fallback-bc, oklch(var(--bc)/.8));
-}
\ No newline at end of file
diff --git a/pkg/view/src/pages/realms/realm.tsx b/pkg/view/src/pages/realms/realm.tsx
deleted file mode 100644
index b655aba..0000000
--- a/pkg/view/src/pages/realms/realm.tsx
+++ /dev/null
@@ -1,291 +0,0 @@
-import { createSignal, Show } from "solid-js";
-import { createStore } from "solid-js/store";
-import { useNavigate, useParams } from "@solidjs/router";
-import { request } from "../../scripts/request.ts";
-
-import PostList from "../../components/posts/PostList.tsx";
-import PostPublish from "../../components/posts/PostPublish.tsx";
-
-import styles from "./realm.module.css";
-import { getAtk, useUserinfo } from "../../stores/userinfo.tsx";
-import { closeModel, openModel } from "../../scripts/modals.ts";
-
-export default function RealmPage() {
- const userinfo = useUserinfo();
-
- const [error, setError] = createSignal(null);
- const [submitting, setSubmitting] = createSignal(false);
-
- const [realm, setRealm] = createSignal(null);
- const [page, setPage] = createSignal(0);
- const [info, setInfo] = createSignal(null);
-
- const params = useParams();
- const navigate = useNavigate();
-
- async function readRealm() {
- const res = await request(`/api/realms/${params["realmId"]}`);
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- setRealm(await res.json());
- }
- }
-
- readRealm();
-
- async function readPosts(pn?: number) {
- if (pn) setPage(pn);
- const res = await request(`/api/posts?` + new URLSearchParams({
- take: (10).toString(),
- offset: ((page() - 1) * 10).toString(),
- realmId: params["realmId"]
- }));
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- setError(null);
- setInfo(await res.json());
- }
- }
-
- async function editRealm(evt: SubmitEvent) {
- evt.preventDefault();
-
- const form = evt.target as HTMLFormElement;
- const data = Object.fromEntries(new FormData(form));
-
- setSubmitting(true);
- const res = await request(`/api/realms/${params["realmId"]}`, {
- method: "PUT",
- headers: { "Authorization": `Bearer ${getAtk()}`, "Content-Type": "application/json" },
- body: JSON.stringify({
- name: data.name,
- description: data.description,
- is_public: data.is_public != null
- })
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- await readRealm();
- closeModel("#edit-realm");
- form.reset();
- }
- setSubmitting(false);
- }
-
- async function inviteMember(evt: SubmitEvent) {
- evt.preventDefault();
-
- const form = evt.target as HTMLFormElement;
- const data = Object.fromEntries(new FormData(form));
-
- setSubmitting(true);
- const res = await request(`/api/realms/${params["realmId"]}/invite`, {
- method: "POST",
- headers: { "Authorization": `Bearer ${getAtk()}`, "Content-Type": "application/json" },
- body: JSON.stringify(data)
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- await readRealm();
- closeModel("#invite-member");
- form.reset();
- }
- setSubmitting(false);
- }
-
- async function kickMember(evt: SubmitEvent) {
- evt.preventDefault();
-
- const form = evt.target as HTMLFormElement;
- const data = Object.fromEntries(new FormData(form));
-
- setSubmitting(true);
- const res = await request(`/api/realms/${params["realmId"]}/kick`, {
- method: "POST",
- headers: { "Authorization": `Bearer ${getAtk()}`, "Content-Type": "application/json" },
- body: JSON.stringify(data)
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- await readRealm();
- closeModel("#kick-member");
- form.reset();
- }
- setSubmitting(false);
- }
-
- async function breakRealm() {
- if (!confirm("Are you sure about that? All posts in this realm will disappear forever.")) return;
-
- const res = await request(`/api/realms/${params["realmId"]}`, {
- method: "DELETE",
- headers: { "Authorization": `Bearer ${getAtk()}` }
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- navigate("/realms");
- }
- }
-
- function setMeta(data: any, field: string, scroll = true) {
- const meta: { [id: string]: any } = {
- reposting: null,
- replying: null,
- editing: null
- };
- meta[field] = data;
- setPublishMeta(meta);
-
- if (scroll) window.scroll({ top: 0, behavior: "smooth" });
- }
-
- const [publishMeta, setPublishMeta] = createStore({
- replying: null,
- reposting: null,
- editing: null
- });
-
- return (
- <>
-
-
-
-
{realm()?.name}
-
{realm()?.description}
-
-
-
Realm #{realm()?.id}
-
-
-
-
-
-
-
-
-
-
-
- setMeta(null, "none", false)}
- onPost={() => readPosts()}
- onError={setError}
- />
-
- setMeta(item, "reposting")}
- onReply={(item) => setMeta(item, "replying")}
- onEdit={(item) => setMeta(item, "editing")}
- />
-
-
-
-
-
-
- >
- );
-}
\ No newline at end of file
diff --git a/pkg/view/src/pages/search.tsx b/pkg/view/src/pages/search.tsx
deleted file mode 100644
index 9aeea23..0000000
--- a/pkg/view/src/pages/search.tsx
+++ /dev/null
@@ -1,124 +0,0 @@
-import { useNavigate, useSearchParams } from "@solidjs/router";
-import { createSignal, Show } from "solid-js";
-import { createStore } from "solid-js/store";
-import { closeModel, openModel } from "../scripts/modals.ts";
-import { request } from "../scripts/request.ts";
-import PostPublish from "../components/posts/PostPublish.tsx";
-import PostList from "../components/posts/PostList.tsx";
-
-export default function SearchPage() {
- const [searchParams] = useSearchParams();
-
- const [error, setError] = createSignal(null);
-
- const [page, setPage] = createSignal(0);
- const [info, setInfo] = createSignal(null);
-
- const navigate = useNavigate();
-
- async function readPosts(pn?: number) {
- if (pn) setPage(pn);
- const res = await request("/api/posts?" + new URLSearchParams({
- take: (10).toString(),
- offset: ((page() - 1) * 10).toString(),
- ...searchParams
- }));
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- setError(null);
- setInfo(await res.json());
- }
- }
-
- function setMeta(data: any, field: string, open = true) {
- const meta: { [id: string]: any } = {
- reposting: null,
- replying: null,
- editing: null
- };
- meta[field] = data;
- setPublishMeta(meta);
-
- if (open) openModel("#post-publish");
- else closeModel("#post-publish");
- }
-
- const [publishMeta, setPublishMeta] = createStore({
- replying: null,
- reposting: null,
- editing: null
- });
-
- function getDescribe() {
- let builder = [];
- if (searchParams["category"]) {
- builder.push("category is #" + searchParams["category"]);
- } else if (searchParams["tag"]) {
- builder.push("tag is #" + searchParams["tag"]);
- }
-
- return builder.join(" and ");
- }
-
- function back() {
- if (window.history.length > 0) {
- window.history.back();
- } else {
- navigate("/");
- }
- }
-
- return (
- <>
-
-
-
-
-
-
- You will only see posts with {getDescribe()}
-
-
-
-
- setMeta(item, "reposting")}
- onReply={(item) => setMeta(item, "replying")}
- onEdit={(item) => setMeta(item, "editing")}
- />
- >
- );
-}
\ No newline at end of file
diff --git a/pkg/view/src/pages/view.module.css b/pkg/view/src/pages/view.module.css
deleted file mode 100644
index b5b7d39..0000000
--- a/pkg/view/src/pages/view.module.css
+++ /dev/null
@@ -1,11 +0,0 @@
-.wrapper {
- display: grid;
- grid-template-columns: 1fr;
- column-gap: 20px;
-}
-
-@media (min-width: 1024px) {
- .wrapper {
- grid-template-columns: 1fr 2fr 1fr;
- }
-}
diff --git a/pkg/view/src/pages/view.tsx b/pkg/view/src/pages/view.tsx
deleted file mode 100644
index 46b6530..0000000
--- a/pkg/view/src/pages/view.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { createMemo } from "solid-js";
-import { useSearchParams } from "@solidjs/router";
-
-import styles from "./view.module.css";
-
-export default function FeedView(props: any) {
- const [searchParams] = useSearchParams();
-
- const scrollContentStyles = createMemo(() => {
- if (!searchParams["embedded"]) {
- return "max-md:mb-[64px]";
- } else {
- return "h-[100vh]";
- }
- });
-
- return (
-
-
-
-
- {props.children}
-
-
-
-
- );
-}
diff --git a/pkg/view/src/scripts/modals.ts b/pkg/view/src/scripts/modals.ts
deleted file mode 100644
index 9a7ea16..0000000
--- a/pkg/view/src/scripts/modals.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export function openModel(selector: string) {
- document.querySelector(selector)?.showModal()
-}
-
-export function closeModel(selector: string) {
- document.querySelector(selector)?.close()
-}
\ No newline at end of file
diff --git a/pkg/view/src/scripts/request.ts b/pkg/view/src/scripts/request.ts
deleted file mode 100644
index b85771b..0000000
--- a/pkg/view/src/scripts/request.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export async function request(input: string, init?: RequestInit) {
- const prefix = window.__LAUNCHPAD_TARGET__ ?? "";
- return await fetch(prefix + input, init)
-}
\ No newline at end of file
diff --git a/pkg/view/src/stores/userinfo.tsx b/pkg/view/src/stores/userinfo.tsx
deleted file mode 100644
index 1a02313..0000000
--- a/pkg/view/src/stores/userinfo.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import Cookie from "universal-cookie";
-import { createContext, useContext } from "solid-js";
-import { createStore } from "solid-js/store";
-import { request } from "../scripts/request.ts";
-
-export interface Userinfo {
- isLoggedIn: boolean,
- displayName: string,
- profiles: any,
-}
-
-const UserinfoContext = createContext();
-
-const defaultUserinfo: Userinfo = {
- isLoggedIn: false,
- displayName: "Citizen",
- profiles: null
-};
-
-const [userinfo, setUserinfo] = createStore(structuredClone(defaultUserinfo));
-
-export function getAtk(): string {
- return new Cookie().get("identity_auth_key");
-}
-
-function checkLoggedIn(): boolean {
- return new Cookie().get("identity_auth_key");
-}
-
-export async function readProfiles() {
- if (!checkLoggedIn()) return;
-
- const res = await request("/api/users/me", {
- headers: { "Authorization": `Bearer ${getAtk()}` }
- });
-
- if (res.status !== 200) {
- clearUserinfo();
- window.location.reload();
- }
-
- const data = await res.json();
-
- setUserinfo({
- isLoggedIn: true,
- displayName: data["name"],
- profiles: data
- });
-}
-
-export function clearUserinfo() {
- const cookies = document.cookie.split(";");
- for (let i = 0; i < cookies.length; i++) {
- const cookie = cookies[i];
- const eqPos = cookie.indexOf("=");
- const name = eqPos > -1 ? cookie.substring(0, eqPos) : cookie;
- document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
- }
-
- setUserinfo(defaultUserinfo);
-}
-
-export function UserinfoProvider(props: any) {
- return (
-
- {props.children}
-
- );
-}
-
-export function useUserinfo() {
- return useContext(UserinfoContext);
-}
\ No newline at end of file
diff --git a/pkg/view/src/stores/wellKnown.tsx b/pkg/view/src/stores/wellKnown.tsx
deleted file mode 100644
index 42bebf3..0000000
--- a/pkg/view/src/stores/wellKnown.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { createContext, useContext } from "solid-js";
-import { createStore } from "solid-js/store";
-import { request } from "../scripts/request.ts";
-
-const WellKnownContext = createContext();
-
-const [wellKnown, setWellKnown] = createStore(null);
-
-export async function readWellKnown() {
- const res = await request("/.well-known")
- setWellKnown(await res.json())
-}
-
-export function WellKnownProvider(props: any) {
- return (
-
- {props.children}
-
- );
-}
-
-export function useWellKnown() {
- return useContext(WellKnownContext);
-}
\ No newline at end of file
diff --git a/pkg/view/tailwind.config.js b/pkg/view/tailwind.config.js
deleted file mode 100644
index 12f2509..0000000
--- a/pkg/view/tailwind.config.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/** @type {import("tailwindcss").Config} */
-export default {
- content: [
- "./src/**/*.{js,jsx,ts,tsx}"
- ],
- daisyui: {
- themes: [
- {
- light: {
- ...require("daisyui/src/theming/themes")["light"],
- primary: "#4750a3",
- secondary: "#93c5fd",
- accent: "#0f766e",
- info: "#67e8f9",
- success: "#15803d",
- warning: "#f97316",
- error: "#dc2626",
- "--rounded-box": "0",
- "--rounded-btn": "0",
- "--rounded-badge": "0",
- "--tab-radius": "0"
- }
- },
- {
- dark: {
- ...require("daisyui/src/theming/themes")["dark"],
- primary: "#4750a3",
- secondary: "#93c5fd",
- accent: "#0f766e",
- info: "#67e8f9",
- success: "#15803d",
- warning: "#f97316",
- error: "#dc2626",
- "--rounded-box": "0",
- "--rounded-btn": "0",
- "--rounded-badge": "0",
- "--tab-radius": "0"
- }
- }
- ]
- },
- plugins: [require("daisyui"), require("@tailwindcss/typography")]
-};
-
diff --git a/pkg/view/tsconfig.json b/pkg/view/tsconfig.json
deleted file mode 100644
index b5ebb60..0000000
--- a/pkg/view/tsconfig.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "compilerOptions": {
- "target": "ES2020",
- "useDefineForClassFields": true,
- "module": "ESNext",
- "lib": ["ES2020", "ES2015", "DOM", "DOM.Iterable"],
- "skipLibCheck": true,
-
- /* Bundler mode */
- "moduleResolution": "bundler",
- "allowImportingTsExtensions": true,
- "resolveJsonModule": true,
- "isolatedModules": true,
- "noEmit": true,
- "jsx": "preserve",
- "jsxImportSource": "solid-js",
-
- /* Linting */
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noFallthroughCasesInSwitch": true
- },
- "include": ["src"],
- "references": [{ "path": "./tsconfig.node.json" }]
-}
diff --git a/pkg/view/tsconfig.node.json b/pkg/view/tsconfig.node.json
deleted file mode 100644
index 42872c5..0000000
--- a/pkg/view/tsconfig.node.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "compilerOptions": {
- "composite": true,
- "skipLibCheck": true,
- "module": "ESNext",
- "moduleResolution": "bundler",
- "allowSyntheticDefaultImports": true
- },
- "include": ["vite.config.ts"]
-}
diff --git a/pkg/view/vite.config.ts b/pkg/view/vite.config.ts
deleted file mode 100644
index dec5cb7..0000000
--- a/pkg/view/vite.config.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { defineConfig } from "vite";
-import solid from "vite-plugin-solid";
-import devtools from "solid-devtools/vite";
-
-export default defineConfig({
- plugins: [devtools({ autoname: true }), solid()],
- server: {
- proxy: {
- "/api": "http://localhost:8445",
- "/.well-known": "http://localhost:8445"
- }
- }
-});
diff --git a/pkg/views/.eslintrc.cjs b/pkg/views/.eslintrc.cjs
new file mode 100644
index 0000000..6f40582
--- /dev/null
+++ b/pkg/views/.eslintrc.cjs
@@ -0,0 +1,15 @@
+/* eslint-env node */
+require('@rushstack/eslint-patch/modern-module-resolution')
+
+module.exports = {
+ root: true,
+ 'extends': [
+ 'plugin:vue/vue3-essential',
+ 'eslint:recommended',
+ '@vue/eslint-config-typescript',
+ '@vue/eslint-config-prettier/skip-formatting'
+ ],
+ parserOptions: {
+ ecmaVersion: 'latest'
+ }
+}
diff --git a/pkg/views/.gitignore b/pkg/views/.gitignore
new file mode 100644
index 0000000..8ee54e8
--- /dev/null
+++ b/pkg/views/.gitignore
@@ -0,0 +1,30 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+*.tsbuildinfo
diff --git a/pkg/views/.prettierrc.json b/pkg/views/.prettierrc.json
new file mode 100644
index 0000000..88cdad7
--- /dev/null
+++ b/pkg/views/.prettierrc.json
@@ -0,0 +1,8 @@
+{
+ "$schema": "https://json.schemastore.org/prettierrc",
+ "semi": false,
+ "tabWidth": 2,
+ "singleQuote": false,
+ "printWidth": 120,
+ "trailingComma": "none"
+}
diff --git a/pkg/views/.vscode/extensions.json b/pkg/views/.vscode/extensions.json
new file mode 100644
index 0000000..009a534
--- /dev/null
+++ b/pkg/views/.vscode/extensions.json
@@ -0,0 +1,8 @@
+{
+ "recommendations": [
+ "Vue.volar",
+ "Vue.vscode-typescript-vue-plugin",
+ "dbaeumer.vscode-eslint",
+ "esbenp.prettier-vscode"
+ ]
+}
diff --git a/pkg/views/README.md b/pkg/views/README.md
new file mode 100644
index 0000000..f25fd02
--- /dev/null
+++ b/pkg/views/README.md
@@ -0,0 +1,46 @@
+# views
+
+This template should help get you started developing with Vue 3 in Vite.
+
+## Recommended IDE Setup
+
+[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
+
+## Type Support for `.vue` Imports in TS
+
+TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
+
+If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
+
+1. Disable the built-in TypeScript Extension
+ 1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
+ 2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
+2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
+
+## Customize configuration
+
+See [Vite Configuration Reference](https://vitejs.dev/config/).
+
+## Project Setup
+
+```sh
+npm install
+```
+
+### Compile and Hot-Reload for Development
+
+```sh
+npm run dev
+```
+
+### Type-Check, Compile and Minify for Production
+
+```sh
+npm run build
+```
+
+### Lint with [ESLint](https://eslint.org/)
+
+```sh
+npm run lint
+```
diff --git a/pkg/views/bun.lockb b/pkg/views/bun.lockb
new file mode 100755
index 0000000..a5a3806
Binary files /dev/null and b/pkg/views/bun.lockb differ
diff --git a/pkg/view/src/vite-env.d.ts b/pkg/views/env.d.ts
similarity index 100%
rename from pkg/view/src/vite-env.d.ts
rename to pkg/views/env.d.ts
diff --git a/pkg/views/index.html b/pkg/views/index.html
new file mode 100644
index 0000000..f2b3b63
--- /dev/null
+++ b/pkg/views/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Goatplaza
+
+
+
+
+
+
diff --git a/pkg/views/package.json b/pkg/views/package.json
new file mode 100644
index 0000000..69649f9
--- /dev/null
+++ b/pkg/views/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "views",
+ "version": "0.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "run-p type-check \"build-only {@}\" --",
+ "preview": "vite preview",
+ "build-only": "vite build",
+ "type-check": "vue-tsc --build --force",
+ "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
+ "format": "prettier --write src/"
+ },
+ "dependencies": {
+ "@mdi/font": "^7.4.47",
+ "@unocss/reset": "^0.58.5",
+ "pinia": "^2.1.7",
+ "unocss": "^0.58.5",
+ "vue": "^3.4.15",
+ "vue-router": "^4.2.5",
+ "vuetify": "^3.5.7"
+ },
+ "devDependencies": {
+ "@rushstack/eslint-patch": "^1.3.3",
+ "@tsconfig/node20": "^20.1.2",
+ "@types/node": "^20.11.10",
+ "@vitejs/plugin-vue": "^5.0.3",
+ "@vitejs/plugin-vue-jsx": "^3.1.0",
+ "@vue/eslint-config-prettier": "^8.0.0",
+ "@vue/eslint-config-typescript": "^12.0.0",
+ "@vue/tsconfig": "^0.5.1",
+ "eslint": "^8.49.0",
+ "eslint-plugin-vue": "^9.17.0",
+ "npm-run-all2": "^6.1.1",
+ "prettier": "^3.0.3",
+ "typescript": "~5.3.0",
+ "vite": "^5.0.11",
+ "vue-tsc": "^1.8.27"
+ }
+}
diff --git a/pkg/views/src/index.vue b/pkg/views/src/index.vue
new file mode 100644
index 0000000..4f21c35
--- /dev/null
+++ b/pkg/views/src/index.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pkg/views/src/layouts/master.vue b/pkg/views/src/layouts/master.vue
new file mode 100644
index 0000000..9db7e29
--- /dev/null
+++ b/pkg/views/src/layouts/master.vue
@@ -0,0 +1,27 @@
+
+
+
+
Goatplaza
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pkg/views/src/main.ts b/pkg/views/src/main.ts
new file mode 100644
index 0000000..aa2849e
--- /dev/null
+++ b/pkg/views/src/main.ts
@@ -0,0 +1,41 @@
+import "virtual:uno.css"
+
+import { createApp } from "vue"
+import { createPinia } from "pinia"
+
+import "vuetify/styles"
+import { createVuetify } from "vuetify"
+import * as components from "vuetify/components"
+import * as directives from "vuetify/directives"
+
+import "@mdi/font/css/materialdesignicons.min.css"
+
+import index from "./index.vue"
+import router from "./router"
+
+const app = createApp(index)
+
+app.use(
+ createVuetify({
+ components,
+ directives,
+ theme: {
+ themes: {
+ light: {
+ primary: "#4a5099",
+ secondary: "#2196f3",
+ accent: "#009688",
+ error: "#f44336",
+ warning: "#ff9800",
+ info: "#03a9f4",
+ success: "#4caf50"
+ }
+ }
+ }
+ })
+)
+
+app.use(createPinia())
+app.use(router)
+
+app.mount("#app")
diff --git a/pkg/views/src/router/index.ts b/pkg/views/src/router/index.ts
new file mode 100644
index 0000000..6963185
--- /dev/null
+++ b/pkg/views/src/router/index.ts
@@ -0,0 +1,22 @@
+import { createRouter, createWebHistory } from "vue-router"
+import MasterLayout from "@/layouts/master.vue"
+import LandingPage from "@/views/landing.vue"
+
+const router = createRouter({
+ history: createWebHistory(import.meta.env.BASE_URL),
+ routes: [
+ {
+ path: "/",
+ component: MasterLayout,
+ children: [
+ {
+ path: "/",
+ name: "landing",
+ component: LandingPage
+ }
+ ]
+ }
+ ]
+})
+
+export default router
diff --git a/pkg/views/src/views/landing.vue b/pkg/views/src/views/landing.vue
new file mode 100644
index 0000000..a02994a
--- /dev/null
+++ b/pkg/views/src/views/landing.vue
@@ -0,0 +1,3 @@
+
+ Good morning!
+
\ No newline at end of file
diff --git a/pkg/views/tsconfig.app.json b/pkg/views/tsconfig.app.json
new file mode 100644
index 0000000..e14c754
--- /dev/null
+++ b/pkg/views/tsconfig.app.json
@@ -0,0 +1,14 @@
+{
+ "extends": "@vue/tsconfig/tsconfig.dom.json",
+ "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
+ "exclude": ["src/**/__tests__/*"],
+ "compilerOptions": {
+ "composite": true,
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ }
+}
diff --git a/pkg/views/tsconfig.json b/pkg/views/tsconfig.json
new file mode 100644
index 0000000..66b5e57
--- /dev/null
+++ b/pkg/views/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "files": [],
+ "references": [
+ {
+ "path": "./tsconfig.node.json"
+ },
+ {
+ "path": "./tsconfig.app.json"
+ }
+ ]
+}
diff --git a/pkg/views/tsconfig.node.json b/pkg/views/tsconfig.node.json
new file mode 100644
index 0000000..f094063
--- /dev/null
+++ b/pkg/views/tsconfig.node.json
@@ -0,0 +1,19 @@
+{
+ "extends": "@tsconfig/node20/tsconfig.json",
+ "include": [
+ "vite.config.*",
+ "vitest.config.*",
+ "cypress.config.*",
+ "nightwatch.conf.*",
+ "playwright.config.*"
+ ],
+ "compilerOptions": {
+ "composite": true,
+ "noEmit": true,
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "types": ["node"]
+ }
+}
diff --git a/pkg/views/uno.config.ts b/pkg/views/uno.config.ts
new file mode 100644
index 0000000..bdfffe1
--- /dev/null
+++ b/pkg/views/uno.config.ts
@@ -0,0 +1,5 @@
+import { defineConfig, presetUno } from "unocss"
+
+export default defineConfig({
+ presets: [presetUno({ preflight: false })]
+})
diff --git a/pkg/views/vite.config.ts b/pkg/views/vite.config.ts
new file mode 100644
index 0000000..7dbfdfa
--- /dev/null
+++ b/pkg/views/vite.config.ts
@@ -0,0 +1,16 @@
+import { fileURLToPath, URL } from "node:url"
+
+import { defineConfig } from "vite"
+import vue from "@vitejs/plugin-vue"
+import vueJsx from "@vitejs/plugin-vue-jsx"
+import unocss from "unocss/vite"
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [vue(), vueJsx(), unocss()],
+ resolve: {
+ alias: {
+ "@": fileURLToPath(new URL("./src", import.meta.url))
+ }
+ }
+})