Compare commits

...

10 Commits

Author SHA1 Message Date
Aumetra Weisman 7282068f54
Merge e5da0b2131 into 5f79096014 2024-05-05 16:03:47 +00:00
Aumetra Weisman e5da0b2131
format codebase 2024-05-05 18:03:43 +02:00
vesdev 4299c333ba
new layout 2024-05-05 17:59:46 +02:00
Aumetra Weisman f78688a55d
Merge branch 'main' into svelte5-frontend 2024-05-05 15:38:01 +02:00
Aumetra Weisman 3acad3500d add register functionality 2024-05-03 00:25:25 +02:00
Aumetra Weisman fdb333a7ab frontpage overhaul 2024-05-03 00:25:25 +02:00
Aumetra Weisman c81fb6c3d1 add unplugin-icons 2024-05-03 00:25:25 +02:00
Aumetra Weisman 7ba9db488a add graphql client 2024-05-03 00:25:25 +02:00
Aumetra Weisman 9386f1742a rewrite frontpage 2024-05-03 00:25:25 +02:00
Aumetra Weisman 6f79177bae begin port 2024-05-03 00:25:25 +02:00
99 changed files with 6497 additions and 353 deletions

352
flake.nix
View File

@ -26,169 +26,209 @@
# like so `nix build --override-input debugBuild github:boolean-option/true`
debugBuild.url = "github:boolean-option/false/d06b4794a134686c70a1325df88a6e6768c6b212";
};
outputs = { self, devenv, flake-utils, nixpkgs, rust-overlay, crane, ... } @ inputs:
(flake-utils.lib.eachDefaultSystem
(system:
let
features = "--all-features";
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs {
inherit overlays system;
};
stdenv = pkgs.stdenvAdapters.useMoldLinker pkgs.stdenv;
rustPlatform = pkgs.makeRustPlatform {
cargo = pkgs.rust-bin.stable.latest.minimal;
rustc = pkgs.rust-bin.stable.latest.minimal;
inherit stdenv;
};
craneLib = (crane.mkLib pkgs).overrideToolchain pkgs.rust-bin.stable.latest.minimal;
buildInputs = with pkgs; [
openssl
sqlite
zlib
];
nativeBuildInputs = with pkgs; [
protobuf
pkg-config
rustPlatform.bindgenHook
];
src = pkgs.lib.cleanSourceWith {
src = pkgs.lib.cleanSource ./.;
filter = name: type:
let baseName = baseNameOf (toString name);
in !(baseName == "flake.lock" || pkgs.lib.hasSuffix ".nix" baseName);
};
commonArgs = {
inherit src stdenv buildInputs nativeBuildInputs;
strictDeps = true;
meta = {
description = "ActivityPub-federated microblogging";
homepage = "https://joinkitsune.org";
outputs =
{ self
, devenv
, flake-utils
, nixpkgs
, rust-overlay
, crane
, ...
}@inputs:
(
flake-utils.lib.eachDefaultSystem
(
system:
let
features = "--all-features";
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs { inherit overlays system; };
stdenv = pkgs.stdenvAdapters.useMoldLinker pkgs.stdenv;
rustPlatform = pkgs.makeRustPlatform {
cargo = pkgs.rust-bin.stable.latest.minimal;
rustc = pkgs.rust-bin.stable.latest.minimal;
inherit stdenv;
};
OPENSSL_NO_VENDOR = 1;
NIX_OUTPATH_USED_AS_RANDOM_SEED = "aaaaaaaaaa";
cargoExtraArgs = "--locked ${features}";
} // (pkgs.lib.optionalAttrs inputs.debugBuild.value {
# do a debug build, as `dev` is the default debug profile
CARGO_PROFILE = "dev";
});
craneLib = (crane.mkLib pkgs).overrideToolchain pkgs.rust-bin.stable.latest.minimal;
buildInputs = with pkgs; [
openssl
sqlite
zlib
];
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
version = cargoToml.workspace.package.version;
nativeBuildInputs = with pkgs; [
protobuf
pkg-config
rustPlatform.bindgenHook
];
cargoArtifacts = craneLib.buildDepsOnly (commonArgs // {
pname = "kitsune-workspace";
src = craneLib.cleanCargoSource src;
});
in
{
formatter = pkgs.nixpkgs-fmt;
packages = rec {
default = main;
cli = craneLib.buildPackage (commonArgs // {
pname = "kitsune-cli";
cargoExtraArgs = commonArgs.cargoExtraArgs + " --bin kitsune-cli";
inherit cargoArtifacts;
doCheck = false;
});
mrf-tool = craneLib.buildPackage (commonArgs // {
pname = "mrf-tool";
cargoExtraArgs = commonArgs.cargoExtraArgs + " --bin mrf-tool";
inherit cargoArtifacts;
doCheck = false;
});
main = craneLib.buildPackage (commonArgs // rec {
pname = "kitsune";
cargoExtraArgs = commonArgs.cargoExtraArgs + " --bin kitsune --bin kitsune-job-runner";
inherit cargoArtifacts;
doCheck = false;
});
frontend = pkgs.mkYarnPackage {
inherit version;
packageJSON = "${src}/kitsune-fe/package.json";
yarnLock = "${src}/kitsune-fe/yarn.lock";
src = "${src}/kitsune-fe";
buildPhase = ''
export HOME=$(mktemp -d)
yarn --offline build
'';
installPhase = ''
mkdir -p $out
cp -R deps/kitsune-fe/dist $out
'';
distPhase = "true";
};
};
devShells = rec {
default = backend;
backend = devenv.lib.mkShell {
inherit pkgs inputs;
modules = [
({ pkgs, ... }: {
packages = with pkgs; [
cargo-insta
diesel-cli
rust-bin.stable.latest.default
]
++
buildInputs ++ nativeBuildInputs;
enterShell = ''
export PG_HOST=127.0.0.1
export PG_PORT=5432
[ -z "$DATABASE_URL" ] && export DATABASE_URL=postgres://$USER@$PG_HOST:$PG_PORT/$USER
export REDIS_PORT=6379
[ -z "$REDIS_URL" ] && export REDIS_URL="redis://127.0.0.1:$REDIS_PORT"
'';
services = {
postgres = {
enable = true;
listen_addresses = "127.0.0.1";
};
redis.enable = true;
};
})
];
src = pkgs.lib.cleanSourceWith {
src = pkgs.lib.cleanSource ./.;
filter =
name: type:
let
baseName = baseNameOf (toString name);
in
!(baseName == "flake.lock" || pkgs.lib.hasSuffix ".nix" baseName);
};
frontend = pkgs.mkShell {
buildInputs = with pkgs; [
nodejs
yarn
];
};
};
}
) // {
overlays = rec {
default = kitsune;
kitsune = (import ./overlay.nix self);
};
commonArgs =
{
inherit
src
stdenv
buildInputs
nativeBuildInputs
;
nixosModules = rec {
default = kitsune;
kitsune = (import ./module.nix);
};
}) // {
strictDeps = true;
meta = {
description = "ActivityPub-federated microblogging";
homepage = "https://joinkitsune.org";
};
OPENSSL_NO_VENDOR = 1;
NIX_OUTPATH_USED_AS_RANDOM_SEED = "aaaaaaaaaa";
cargoExtraArgs = "--locked ${features}";
}
// (pkgs.lib.optionalAttrs inputs.debugBuild.value {
# do a debug build, as `dev` is the default debug profile
CARGO_PROFILE = "dev";
});
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
version = cargoToml.workspace.package.version;
cargoArtifacts = craneLib.buildDepsOnly (
commonArgs
// {
pname = "kitsune-workspace";
src = craneLib.cleanCargoSource src;
}
);
in
{
formatter = pkgs.nixpkgs-fmt;
packages = rec {
default = main;
cli = craneLib.buildPackage (
commonArgs
// {
pname = "kitsune-cli";
cargoExtraArgs = commonArgs.cargoExtraArgs + " --bin kitsune-cli";
inherit cargoArtifacts;
doCheck = false;
}
);
mrf-tool = craneLib.buildPackage (
commonArgs
// {
pname = "mrf-tool";
cargoExtraArgs = commonArgs.cargoExtraArgs + " --bin mrf-tool";
inherit cargoArtifacts;
doCheck = false;
}
);
main = craneLib.buildPackage (
commonArgs
// {
pname = "kitsune";
cargoExtraArgs = commonArgs.cargoExtraArgs + " --bin kitsune --bin kitsune-job-runner";
inherit cargoArtifacts;
doCheck = false;
}
);
frontend = pkgs.mkYarnPackage {
inherit version;
packageJSON = "${src}/kitsune-fe/package.json";
yarnLock = "${src}/kitsune-fe/yarn.lock";
src = "${src}/kitsune-fe";
buildPhase = ''
export HOME=$(mktemp -d)
yarn --offline build
'';
installPhase = ''
mkdir -p $out
cp -R deps/kitsune-fe/dist $out
'';
distPhase = "true";
};
};
devShells = rec {
default = backend;
backend = devenv.lib.mkShell {
inherit pkgs inputs;
modules = [
(
{ pkgs, ... }:
{
packages =
with pkgs;
[
cargo-insta
diesel-cli
rust-bin.stable.latest.default
]
++ buildInputs
++ nativeBuildInputs;
enterShell = ''
export PG_HOST=127.0.0.1
export PG_PORT=5432
[ -z "$DATABASE_URL" ] && export DATABASE_URL=postgres://$USER@$PG_HOST:$PG_PORT/$USER
export REDIS_PORT=6379
[ -z "$REDIS_URL" ] && export REDIS_URL="redis://127.0.0.1:$REDIS_PORT"
'';
services = {
postgres = {
enable = true;
listen_addresses = "127.0.0.1";
};
redis.enable = true;
};
}
)
];
};
frontend = pkgs.mkShell {
buildInputs = with pkgs; [
nodejs
yarn
nodePackages.svelte-language-server
nodePackages.typescript-language-server
];
};
};
}
)
// {
overlays = rec {
default = kitsune;
kitsune = (import ./overlay.nix self);
};
nixosModules = rec {
default = kitsune;
kitsune = (import ./module.nix);
};
}
)
// {
nixci.default = {
debug = {
dir = ".";

24
kitsune-fe-old/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@ -0,0 +1 @@
dist/

View File

@ -0,0 +1,13 @@
{
"endOfLine": "lf",
"importOrder": ["^\\w", "^[./|~/]"],
"importOrderSeparation": true,
"importOrderParserPlugins": ["typescript"],
"plugins": [
"@trivago/prettier-plugin-sort-imports",
"prettier-plugin-css-order"
],
"semi": true,
"singleQuote": true,
"vueIndentScriptAndStyle": true
}

23
kitsune-fe-old/README.md Normal file
View File

@ -0,0 +1,23 @@
# Kitsune FE
Frontend for Kitsune using its GraphQL API
## Build
```
yarn build
```
## Development server
```
yarn dev
```
Set the backend the frontend uses inside `.env.development`
## Autogenerate GraphQL typings in the background
```
yarn graphql-codegen --watch
```

View File

@ -0,0 +1,73 @@
{
"name": "kitsune-fe",
"private": true,
"version": "0.0.0",
"type": "module",
"license": "AGPL-3.0-or-later",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"lint": "eslint src",
"format": "prettier . --write"
},
"dependencies": {
"@fluent/bundle": "^0.18.0",
"@formkit/core": "^1.6.2",
"@formkit/validation": "^1.6.2",
"@formkit/vue": "^1.6.2",
"@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/vue-fontawesome": "^3.0.6",
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
"@mcaptcha/vanilla-glue": "^0.1.0-alpha-3",
"@tiptap/pm": "^2.3.0",
"@tiptap/starter-kit": "^2.3.0",
"@tiptap/vue-3": "^2.3.0",
"@urql/exchange-graphcache": "^7.0.1",
"@urql/vue": "^1.1.3",
"@vueuse/core": "^10.9.0",
"@zxcvbn-ts/core": "^3.0.4",
"@zxcvbn-ts/language-common": "^3.0.4",
"@zxcvbn-ts/language-en": "^3.0.2",
"floating-vue": "^5.2.2",
"fluent-vue": "^3.5.2",
"graphql": "^16.8.1",
"lodash": "^4.17.21",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"rollup": "npm:@rollup/wasm-node",
"tiptap-markdown": "^0.8.10",
"unhead": "^1.9.7",
"vue": "^3.4.23",
"vue-powerglitch": "^1.0.0",
"vue-router": "^4.3.2",
"vue-virtual-scroller": "^2.0.0-beta.8"
},
"devDependencies": {
"@graphql-codegen/cli": "^5.0.2",
"@graphql-codegen/client-preset": "^4.2.5",
"@parcel/watcher": "^2.4.1",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/lodash": "^4.17.0",
"@typescript-eslint/eslint-plugin": "^7.7.0",
"@typescript-eslint/parser": "^7.7.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/eslint-config-prettier": "^9.0.0",
"@vue/eslint-config-typescript": "^13.0.0",
"eslint": "^9.1.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.25.0",
"prettier": "^3.2.5",
"prettier-plugin-css-order": "^2.1.2",
"sass": "^1.75.0",
"typescript": "^5.4.5",
"unplugin-fluent-vue": "^1.3.0",
"vite": "^5.2.10",
"vue-tsc": "^2.0.13"
},
"resolutions": {
"rollup": "npm:@rollup/wasm-node"
}
}

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -0,0 +1,9 @@
// Source: Tailwind <https://tailwindcss.com/docs/screens>
$breakpoints: (
sm: 640px,
md: 768px,
lg: 1024px,
xl: 1280px,
xxl: 1536px,
);

View File

@ -0,0 +1,7 @@
$dark1: #0f1026;
$dark2: #051c30;
$dark3: #042f40;
$shade1dark: #53a0c4;
$shade1light: #afd7fa;
$shade2dark: #935d7e;
$shade2light: #d68fbc;

View File

@ -0,0 +1,8 @@
@use './breakpoints' as *;
@use 'sass:map';
@mixin only-on-mobile {
@media only screen and (max-width: map.get($breakpoints, md)) {
@content;
}
}

View File

@ -0,0 +1,49 @@
@use 'colours' as *;
@use 'fonts' as *;
* {
// font-family: BlockZone;
box-sizing: inherit;
font-family: 'Courier New', Courier, monospace;
}
:root {
background-color: $dark1;
color: white;
color-scheme: light dark;
font-weight: 400;
font-size: 16px;
line-height: 24px;
font-synthesis: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
box-sizing: border-box;
}
a {
color: $shade2light;
font-weight: 500;
text-decoration: inherit;
}
body {
margin: 0;
margin-top: 85px;
}
::-webkit-scrollbar {
width: 0.666em;
height: 0.666em;
}
::-webkit-scrollbar-track {
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
}
::-webkit-scrollbar-thumb {
background-color: $shade2light;
}

View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"noEmit": true
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@ -0,0 +1,32 @@
import vue from '@vitejs/plugin-vue';
import path from 'node:path';
import { ExternalFluentPlugin } from 'unplugin-fluent-vue/vite';
import { defineConfig } from 'vite';
// https://vitejs.dev/config/
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
zxcvbnCommon: ['@zxcvbn-ts/language-common'],
},
},
},
},
plugins: [
vue(),
ExternalFluentPlugin({
locales: ['en', 'en-cyberpunk'],
checkSyntax: true,
baseDir: path.resolve('src'),
ftlDir: path.resolve('src/locales'),
getFtlPath(locale) {
return path.join('src/locales', `${locale}.ftl`);
},
}),
],
});

13
kitsune-fe/.eslintignore Normal file
View File

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

31
kitsune-fe/.eslintrc.cjs Normal file
View File

@ -0,0 +1,31 @@
/** @type { import("eslint").Linter.Config } */
module.exports = {
root: true,
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:svelte/recommended',
'prettier'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020,
extraFileExtensions: ['.svelte']
},
env: {
browser: true,
es2017: true,
node: true
},
overrides: [
{
files: ['*.svelte'],
parser: 'svelte-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser'
}
}
]
};

34
kitsune-fe/.gitignore vendored
View File

@ -1,24 +1,12 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
$houdini

View File

@ -0,0 +1,9 @@
projects:
default:
schema:
- ./schema.graphql
- ./$houdini/graphql/schema.graphql
documents:
- '**/*.gql'
- '**/*.svelte'
- ./$houdini/graphql/documents.gql

1
kitsune-fe/.npmrc Normal file
View File

@ -0,0 +1 @@
engine-strict=true

View File

@ -1 +1,4 @@
dist/
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

View File

@ -1,13 +1,8 @@
{
"endOfLine": "lf",
"importOrder": ["^\\w", "^[./|~/]"],
"importOrderSeparation": true,
"importOrderParserPlugins": ["typescript"],
"plugins": [
"@trivago/prettier-plugin-sort-imports",
"prettier-plugin-css-order"
],
"semi": true,
"singleQuote": true,
"vueIndentScriptAndStyle": true
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}

View File

@ -1,23 +1,38 @@
# Kitsune FE
# create-svelte
Frontend for Kitsune using its GraphQL API
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
## Build
## Creating a project
```
yarn build
If you're seeing this, you've probably already done this step. Congrats!
```bash
# create a new project in the current directory
npm create svelte@latest
# create a new project in my-app
npm create svelte@latest my-app
```
## Development server
## Developing
```
yarn dev
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
Set the backend the frontend uses inside `.env.development`
## Building
## Autogenerate GraphQL typings in the background
To create a production version of your app:
```bash
npm run build
```
yarn graphql-codegen --watch
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.

View File

@ -0,0 +1,18 @@
/// <references types="houdini-svelte">
/** @type {import('houdini').ConfigFile} */
const config = {
watchSchema: {
url: 'http://localhost:5000/graphql'
},
plugins: {
'houdini-svelte': {}
},
scalars: {
DateTime: {
type: 'Date'
}
}
};
export default config;

5248
kitsune-fe/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,73 +1,39 @@
{
"name": "kitsune-fe",
"private": true,
"version": "0.0.0",
"type": "module",
"license": "AGPL-3.0-or-later",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"lint": "eslint src",
"format": "prettier . --write"
},
"dependencies": {
"@fluent/bundle": "^0.18.0",
"@formkit/core": "^1.6.2",
"@formkit/validation": "^1.6.2",
"@formkit/vue": "^1.6.2",
"@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/vue-fontawesome": "^3.0.6",
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
"@mcaptcha/vanilla-glue": "^0.1.0-alpha-3",
"@tiptap/pm": "^2.3.0",
"@tiptap/starter-kit": "^2.3.0",
"@tiptap/vue-3": "^2.3.0",
"@urql/exchange-graphcache": "^7.0.1",
"@urql/vue": "^1.1.3",
"@vueuse/core": "^10.9.0",
"@zxcvbn-ts/core": "^3.0.4",
"@zxcvbn-ts/language-common": "^3.0.4",
"@zxcvbn-ts/language-en": "^3.0.2",
"floating-vue": "^5.2.2",
"fluent-vue": "^3.5.2",
"graphql": "^16.8.1",
"lodash": "^4.17.21",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"rollup": "npm:@rollup/wasm-node",
"tiptap-markdown": "^0.8.10",
"unhead": "^1.9.7",
"vue": "^3.4.23",
"vue-powerglitch": "^1.0.0",
"vue-router": "^4.3.2",
"vue-virtual-scroller": "^2.0.0-beta.8"
},
"devDependencies": {
"@graphql-codegen/cli": "^5.0.2",
"@graphql-codegen/client-preset": "^4.2.5",
"@parcel/watcher": "^2.4.1",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/lodash": "^4.17.0",
"@typescript-eslint/eslint-plugin": "^7.7.0",
"@typescript-eslint/parser": "^7.7.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/eslint-config-prettier": "^9.0.0",
"@vue/eslint-config-typescript": "^13.0.0",
"eslint": "^9.1.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.25.0",
"prettier": "^3.2.5",
"prettier-plugin-css-order": "^2.1.2",
"sass": "^1.75.0",
"typescript": "^5.4.5",
"unplugin-fluent-vue": "^1.3.0",
"vite": "^5.2.10",
"vue-tsc": "^2.0.13"
},
"resolutions": {
"rollup": "npm:@rollup/wasm-node"
}
"name": "kitsune-fe",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test": "vitest",
"lint": "prettier --check . && eslint .",
"format": "prettier --write ."
},
"devDependencies": {
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.1.0",
"@types/eslint": "^8.56.0",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.36.0-next.4",
"houdini": "^0.0.0-20240429154814",
"houdini-svelte": "^0.0.0-20240429154814",
"prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.1.2",
"sass": "^1.76.0",
"svelte": "^5.0.0-next.1",
"svelte-check": "^3.6.0",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"unplugin-icons": "^0.19.0",
"vite": "^5.0.3",
"vitest": "^1.2.0"
},
"type": "module"
}

198
kitsune-fe/schema.graphql Normal file
View File

@ -0,0 +1,198 @@
schema {
query: RootQuery
mutation: RootMutation
}
type Account {
avatar: MediaAttachment
createdAt: DateTime!
displayName: String
header: MediaAttachment
id: UUID!
locked: Boolean!
note: String
posts(after: String, before: String, first: Int, last: Int): PostConnection!
updatedAt: DateTime!
url: String!
username: String!
}
enum CaptchaBackend {
H_CAPTCHA
M_CAPTCHA
}
type CaptchaInfo {
backend: CaptchaBackend!
key: String!
}
"""
A datetime with timezone offset.
The input is a string in RFC3339 format, e.g. "2022-01-12T04:00:19.12345Z"
or "2022-01-12T04:00:19+03:00". The output is also a string in RFC3339
format, but it is always normalized to the UTC (Z) offset, e.g.
"2022-01-12T04:00:19.12345Z".
"""
scalar DateTime
type Instance {
captcha: CaptchaInfo
characterLimit: Int!
description: String!
domain: String!
localPostCount: Int!
name: String!
registrationsOpen: Boolean!
userCount: Int!
version: String!
}
type MediaAttachment {
blurhash: String
contentType: String!
createdAt: DateTime!
description: String
id: UUID!
uploader: Account!
url: String!
}
type OAuth2Application {
createdAt: DateTime!
id: UUID!
name: String!
redirectUri: String!
secret: String!
updatedAt: DateTime!
}
"""
Information about pagination in a connection
"""
type PageInfo {
"""
When paginating forwards, the cursor to continue.
"""
endCursor: String
"""
When paginating forwards, are there more items?
"""
hasNextPage: Boolean!
"""
When paginating backwards, are there more items?
"""
hasPreviousPage: Boolean!
"""
When paginating backwards, the cursor to continue.
"""
startCursor: String
}
type Post {
account: Account!
attachments: [MediaAttachment!]!
content: String!
createdAt: DateTime!
id: UUID!
isSensitive: Boolean!
subject: String
updatedAt: DateTime!
url: String!
visibility: Visibility!
}
type PostConnection {
"""
A list of edges.
"""
edges: [PostEdge!]!
"""
A list of nodes.
"""
nodes: [Post!]!
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type PostEdge {
"""
A cursor for use in pagination
"""
cursor: String!
"""
The item at the end of the edge
"""
node: Post!
}
type RootMutation {
createPost(content: String!, isSensitive: Boolean!, visibility: Visibility!): Post!
deletePost(id: UUID!): UUID!
registerOauthApplication(name: String!, redirectUri: String!): OAuth2Application!
registerUser(captchaToken: String, email: String!, password: String!, username: String!): User!
updateUser(
avatar: Upload
displayName: String
header: Upload
locked: Boolean
note: String
): Account!
}
type RootQuery {
getAccountById(id: UUID!): Account
getPostById(id: UUID!): Post!
homeTimeline(after: String, before: String, first: Int, last: Int): PostConnection!
instance: Instance!
myAccount: Account!
publicTimeline(
after: String
before: String
first: Int
last: Int
onlyLocal: Boolean! = true
): PostConnection!
}
"""
A UUID is a unique 128-bit number, stored as 16 octets. UUIDs are parsed as
Strings within GraphQL. UUIDs are used to assign unique identifiers to
entities without requiring a central allocating authority.
# References
* [Wikipedia: Universally Unique Identifier](http://en.wikipedia.org/wiki/Universally_unique_identifier)
* [RFC4122: A Universally Unique Identifier (UUID) URN Namespace](http://tools.ietf.org/html/rfc4122)
"""
scalar UUID
scalar Upload
type User {
account: Account!
createdAt: DateTime!
email: String!
id: UUID!
updatedAt: DateTime!
username: String!
}
enum Visibility {
FOLLOWER_ONLY
MENTION_ONLY
PUBLIC
UNLISTED
}

13
kitsune-fe/src/app.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};

12
kitsune-fe/src/app.html Normal file
View File

@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

15
kitsune-fe/src/client.ts Normal file
View File

@ -0,0 +1,15 @@
import { HoudiniClient } from '$houdini';
export default new HoudiniClient({
url: `${import.meta.env.VITE_BACKEND_URL ?? ''}/graphql`
// uncomment this to configure the network call (for things like authentication)
// for more information, please visit here: https://www.houdinigraphql.com/guides/authentication
// fetchParams({ session }) {
// return {
// headers: {
// Authentication: `Bearer ${session.token}`,
// }
// }
// }
});

View File

@ -0,0 +1,7 @@
import { describe, it, expect } from 'vitest';
describe('sum test', () => {
it('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3);
});
});

View File

@ -0,0 +1,39 @@
<script lang="ts">
import type { Snippet } from 'svelte';
let {
children,
...rest
}:
| {
children: Snippet;
}
| any = $props();
</script>
<button {...rest}>
{@render children()}
</button>
<style lang="scss">
@use '../../styles/colours' as *;
button {
transition: 0.5s;
cursor: pointer;
border: 0;
border-radius: 10px;
background-color: $shade1dark;
padding: 10px;
width: 100px;
height: 50px;
font-size: 16px;
&:hover {
background-color: $shade2dark;
}
}
</style>

View File

@ -0,0 +1 @@
// place files you want to import through the `$lib` alias in this folder.

View File

@ -0,0 +1,11 @@
<script lang="ts">
import '../styles/root.scss';
const { children } = $props();
</script>
<svelte:head>
<title>Kitsune Ⓐ🏴</title>
</svelte:head>
{@render children()}

View File

@ -0,0 +1 @@
export const ssr = false;

View File

@ -0,0 +1,208 @@
<script lang="ts">
import { graphql } from '$houdini';
import Button from '$lib/components/Button.svelte';
import { onMount } from 'svelte';
let registerButtonDisabled = $state(false);
onMount(() => {
// TODO: Authenticated check and redirect to home timeline
});
async function handleRegister(
event: SubmitEvent & { currentTarget: EventTarget & HTMLFormElement }
) {
event.preventDefault();
const data = new FormData(event.currentTarget);
const username = data.get('username');
const email = data.get('email');
const password = data.get('password');
const passwordConfirmation = data.get('confirm-password');
if (!username) {
alert('Missing username');
return;
} else if (!email) {
alert('Missing email');
return;
} else if (!password) {
alert('Missing password');
return;
} else if (!passwordConfirmation || passwordConfirmation !== password) {
alert('Password mismatch');
return;
}
const register = graphql(`
mutation Register($username: String!, $email: String!, $password: String!) {
registerUser(username: $username, email: $email, password: $password) {
username
createdAt
}
}
`);
try {
const response = await register.mutate({
username: username as string,
email: email as string,
password: password as string
});
if (response.errors) {
alert('Failed to register:\n' + response.errors.map((error) => error.message).concat('\n'));
} else {
alert('Registered!');
}
} catch (ex: unknown) {}
}
</script>
<div class="landing-page">
<div class="section-left">
<div class="section-left-content">
<img class="logo" src="/kitsune_full.svg" />
<h1>Federated microblogging</h1>
Statistics:
<ul>
<li>1,000,000,000 registered users</li>
<li>96,000,000,000 posts</li>
<li>50,000,000 connected instances</li>
</ul>
</div>
</div>
<div class="section-right">
<div class="section-right-content">
<form
class="register-form"
onsubmit={(e) => {
registerButtonDisabled = true;
handleRegister(e).finally(() => (registerButtonDisabled = false));
}}
>
<input placeholder="Username" type="text" name="username" />
<input placeholder="Email" type="email" name="email" />
<input placeholder="Password" type="password" name="password" />
<input placeholder="Confirm Password" type="password" name="confirm-password" />
<p>
<Button
class="register-button"
onclick={() => console.log('register')}
disabled={registerButtonDisabled}
>
Register
</Button>
</p>
</form>
</div>
</div>
</div>
<style lang="scss">
@use '../styles/breakpoints' as *;
@use '../styles/colours' as *;
@use '../styles/mixins' as *;
@use 'sass:map';
.landing-page {
display: flex;
flex-direction: row;
height: 100%;
width: 100%;
.section-right {
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
width: 100%;
background-color: $dark2;
&-content {
border-radius: 0px 30px 30px 0px;
background-color: $dark1;
padding: 2em;
max-width: 50ch;
width: 100%;
}
}
.section-left {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
}
}
.logo {
width: 65%;
}
.register-form {
display: flex;
flex-direction: column;
height: 100%;
& input {
width: 100%;
border: none;
height: 50px;
border-radius: 10px;
background-color: $dark2;
margin-bottom: 0.75em;
margin-top: 0.75em;
padding-left: 1em;
}
& :global(.register-button) {
width: 100%;
margin-top: 2em;
}
}
h1,
ul {
color: $text1;
}
@include only-on-mobile {
.bottom-section {
display: none;
}
.section-left {
display: flex;
flex-direction: column;
align-items: center;
}
.landing-page {
display: flex;
flex-direction: column;
.section-right {
flex-direction: row;
justify-content: center;
&-content {
max-width: 100%;
border-radius: 0px;
background-color: $dark2;
}
}
}
.register-form input {
background-color: $dark1;
}
}
</style>

View File

@ -1,9 +1,9 @@
// Source: Tailwind <https://tailwindcss.com/docs/screens>
$breakpoints: (
sm: 640px,
md: 768px,
lg: 1024px,
xl: 1280px,
xxl: 1536px,
sm: 640px,
md: 768px,
lg: 1024px,
xl: 1280px,
xxl: 1536px
);

View File

@ -1,7 +1,8 @@
$dark1: #0f1026;
$dark2: #051c30;
$dark1: #1c1626;
$dark2: #2b233a;
$dark3: #042f40;
$shade1dark: #53a0c4;
$shade1dark: #ff9e55;
$shade1light: #afd7fa;
$shade2dark: #935d7e;
$shade2light: #d68fbc;
$text1: #e5d4b5;

View File

@ -2,7 +2,7 @@
@use 'sass:map';
@mixin only-on-mobile {
@media only screen and (max-width: map.get($breakpoints, md)) {
@content;
}
@media only screen and (max-width: map.get($breakpoints, md)) {
@content;
}
}

View File

@ -1,49 +1,50 @@
@use 'colours' as *;
@use 'fonts' as *;
* {
// font-family: BlockZone;
box-sizing: inherit;
font-family: 'Courier New', Courier, monospace;
box-sizing: inherit;
font-family: 'Courier New', Courier, monospace;
}
:root {
background-color: $dark1;
color: white;
background-color: $dark1;
color: white;
color-scheme: light dark;
font-weight: 400;
font-size: 16px;
line-height: 24px;
color-scheme: light dark;
font-weight: 400;
font-size: 16px;
line-height: 1.5em;
font-synthesis: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
font-synthesis: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
box-sizing: border-box;
box-sizing: border-box;
}
a {
color: $shade2light;
font-weight: 500;
text-decoration: inherit;
color: $shade2light;
font-weight: 500;
text-decoration: inherit;
}
body {
margin: 0;
margin-top: 85px;
margin: 0;
width: 100vw;
height: 100vh;
overflow-x: hidden;
}
::-webkit-scrollbar {
width: 0.666em;
height: 0.666em;
width: 0.666em;
height: 0.666em;
}
::-webkit-scrollbar-track {
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
}
::-webkit-scrollbar-thumb {
background-color: $shade2light;
background-color: $shade2light;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 434.62 158.92">
<defs>
<style>
.cls-1 {
fill: #ff9e55;
}
.cls-1, .cls-2 {
stroke-width: 0px;
}
.cls-2 {
fill: #e5d4b5;
}
</style>
</defs>
<g id="Layer_1-2" data-name="Layer 1">
<g>
<path class="cls-2" d="M193.84,16.05c.24.28-30.65-11.7-39.07,36.28,0,0-1.42,2.69-5.17,6.72-14.56,5.73-27.13,2.68-27.13,2.68-1.99-3.09-2.45-9.8-2.45-9.8l-11.02-1.22s-3.67-15.92.32-25.94c.01-.02.01-.04.01-.04,1.38-2.23,2.82-4.54,4.3-6.94,15.58-25.25,63.34-21.58,80.21-1.74Z"/>
<path class="cls-1" d="M78.44,121.34c-7.75-9.75-12.09-19.69-14.09-25.12,34.98-3.38,57.64-12.63,71.92-21.65-1.73.14-3.47.22-5.22.22-3.92,0-7.05-.36-9.07-.69-14.15,6.81-34.36,12.94-62.86,15.2l-4.38.35,1.12,4.25c.17.65,4.36,16.22,16.66,31.79,11.22,14.21,31.51,30.98,64.72,30.98.61,0,1.3.05,1.92.04l-.21-7.41c-25.02.45-45.39-8.94-60.5-27.96Z"/>
<path class="cls-1" d="M96.67,51.54c-.48-2.47-1.15-6.58-1.38-11.36-15.19,23.69-24.11,35.13-32.82,40.29V9.68h-7.35v73.79c-5.24,1.29-11.36,1.37-19.4,1.47-1.45.02-2.96.04-4.54.07C10.52,85.36-.3,104.93,0,123.17c.28,16.5,10,34.09,30.7,34.09.2,0,.4,0,.59,0,10.19-.16,18.11-3.41,23.57-9.64,8.86-10.14,7.81-24.57,7.61-26.68v-32.21c10.28-4.45,19.17-14.37,34.2-37.18ZM49.3,142.81c-4.03,4.58-10.12,6.97-18.12,7.1-16.1.22-23.6-13.3-23.83-26.86-.25-15.05,7.89-30.43,23.95-30.7,1.57-.03,3.07-.05,4.51-.06,7.71-.1,13.78-.18,19.31-1.29v30.13l.02.42c.01.13,1.39,13.03-5.84,21.26Z"/>
<g>
<path class="cls-1" d="M169.14,102.93c.04,2.69-.29,6.41-.98,11.14-.69,4.73-1.41,10.59-2.16,17.59-.56,5.31-1.18,13.86-1.85,25.64l-9.76.56c.86-5.91,2.02-14.64,3.48-26.2.79-7.48,1.18-14.38,1.18-20.7,0-2.66-.08-4.82-.22-6.51l10.32-1.51ZM173.18,86.38c0,2.17-.71,4.02-2.13,5.55-1.42,1.53-3.22,2.3-5.39,2.3-3.4,0-5.11-1.81-5.11-5.44,0-2.17.71-4,2.13-5.5,1.42-1.5,3.22-2.24,5.39-2.24,3.4,0,5.11,1.78,5.11,5.33Z"/>
<path class="cls-1" d="M213.12,147.02l-1.4,7.46c-3.93,2.66-7.97,3.98-12.12,3.98-5.12,0-8.7-1.26-10.72-3.79-2.02-2.52-3.03-6.55-3.03-12.09,0-4.64,1.07-14.64,3.2-30.02-1.98,0-5.03.11-9.14.34l1.18-8.14c3.52.22,6.58.34,9.2.34.22-1.53,1.03-6.86,2.41-15.99l8.42-.9c-.67,3.82-1.55,9.44-2.64,16.89,3.48,0,8.45-.19,14.92-.56l-1.12,8.36c-6.17-.22-11.2-.34-15.09-.34-1.95,12.57-2.92,21.56-2.92,26.99,0,4.26.54,7.18,1.63,8.75,1.08,1.57,2.92,2.36,5.5,2.36,3.74,0,7.65-1.22,11.73-3.65Z"/>
<path class="cls-1" d="M259.13,103.6l-2.52,8.13c-3.74-1.38-8.04-2.08-12.9-2.08-7.26,0-10.88,2.43-10.88,7.29,0,1.76.69,3.24,2.08,4.43,1.38,1.2,4.02,2.62,7.91,4.26,9.5,4.08,14.25,9.26,14.25,15.54,0,11.52-7.26,17.28-21.77,17.28-5.87,0-11.3-.5-16.27-1.51l2.58-8.25c4.41,1.65,9.24,2.47,14.47,2.47,8.42,0,12.62-2.41,12.62-7.24,0-3.37-3.37-6.53-10.1-9.48-9.58-4.19-14.36-9.37-14.36-15.54,0-5.35,1.96-9.44,5.89-12.29,3.93-2.84,8.73-4.26,14.42-4.26,6.13,0,11,.41,14.59,1.23Z"/>
<path class="cls-1" d="M317.53,102.87c-2.62,20.8-3.93,34.6-3.93,41.4,0,3.93.3,8.15.9,12.68l-9.48,1.4c-.22-3.29-.34-7.65-.34-13.07h-.45c-4.86,9.09-11.56,13.63-20.08,13.63-9.76,0-14.64-5.55-14.64-16.66,0-2.95.42-8.96,1.26-18.01s1.26-15.8,1.26-20.25l9.71-1.12c0,1.61-.58,6.97-1.74,16.07-1.16,9.11-1.74,15.96-1.74,20.56s.79,7.42,2.38,9.12c1.59,1.7,4.09,2.55,7.49,2.55,4.08,0,7.99-2.67,11.75-8.02,3.76-5.35,5.96-11.22,6.59-17.62.9-9.13,1.35-16.31,1.35-21.54l9.71-1.12Z"/>
<path class="cls-1" d="M376.33,118.92c0,2.96-.42,8.98-1.26,18.07-.84,9.09-1.26,15.86-1.26,20.31l-9.48.56c0-1.65.54-6.94,1.63-15.88,1.08-8.94,1.63-15.71,1.63-20.31s-.79-7.41-2.38-9.12c-1.59-1.7-4.09-2.55-7.49-2.55-4.08,0-7.99,2.66-11.75,7.99s-5.96,11.21-6.59,17.64c-.82,8.38-1.46,15.6-1.91,21.66l-9.15.56c2.62-19.37,3.93-32.46,3.93-39.27,0-3.93-.3-8.71-.9-14.36l9.48-1.4c.22,3.29.34,7.65.34,13.07h.45c4.86-9.09,11.56-13.63,20.09-13.63,9.76,0,14.64,5.55,14.64,16.66Z"/>
<path class="cls-1" d="M434.62,114.6c0,5.05-2.34,8.88-7.01,11.5-5.95,3.37-16.08,5.18-30.41,5.44,0,6.73,1.17,11.62,3.51,14.67,2.34,3.05,6.27,4.57,11.81,4.57s11.33-1.91,17.73-5.72l-1.4,8.3c-5.05,3.63-11.3,5.44-18.74,5.44s-13.25-2.29-16.86-6.87c-3.61-4.58-5.41-10.39-5.41-17.42,0-10.36,3.02-18.45,9.06-24.26,6.04-5.82,13.01-8.72,20.9-8.72,4.79,0,8.79,1.16,12.01,3.48,3.21,2.32,4.82,5.52,4.82,9.59ZM425.7,114.82c0-1.98-.92-3.51-2.75-4.57-1.83-1.07-4.19-1.6-7.07-1.6-4.08,0-7.86,1.35-11.36,4.04-3.5,2.69-5.77,6.83-6.82,12.4,11.48-.22,19.36-1.44,23.62-3.65,2.92-1.5,4.38-3.7,4.38-6.62Z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

View File

@ -0,0 +1,32 @@
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
compilerOptions: {
runes: true
},
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: [vitePreprocess()],
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter({
pages: 'build',
assets: 'build',
fallback: 'index.html',
precompress: false,
strict: true
}),
alias: {
$houdini: './$houdini'
}
}
};
export default config;

View File

@ -1,18 +1,20 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"noEmit": true
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler",
"rootDirs": [".", "./.svelte-kit/types", "./$houdini/types"]
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

View File

@ -1,32 +1,18 @@
import vue from '@vitejs/plugin-vue';
import path from 'node:path';
import { ExternalFluentPlugin } from 'unplugin-fluent-vue/vite';
import { sveltekit } from '@sveltejs/kit/vite';
import houdini from 'houdini/vite';
import { defineConfig } from 'vite';
import Icons from 'unplugin-icons/vite';
// https://vitejs.dev/config/
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
zxcvbnCommon: ['@zxcvbn-ts/language-common'],
},
},
},
},
plugins: [
vue(),
ExternalFluentPlugin({
locales: ['en', 'en-cyberpunk'],
checkSyntax: true,
baseDir: path.resolve('src'),
ftlDir: path.resolve('src/locales'),
getFtlPath(locale) {
return path.join('src/locales', `${locale}.ftl`);
},
}),
],
plugins: [
houdini(),
sveltekit(),
Icons({
autoInstall: true,
compiler: 'svelte'
})
],
test: {
include: ['src/**/*.{test,spec}.{js,ts}']
}
});