Compare commits

..

5 commits

Author SHA1 Message Date
alyssa
23e2cad896 port docs to sveltekit (very broken)
Some checks are pending
Build and push Docker image / .net docker build (push) Waiting to run
.net checks / run .net tests (push) Waiting to run
.net checks / dotnet-format (push) Waiting to run
2025-12-19 22:19:23 -05:00
alyssa
3fbf1513ff add /api/v2/bulk endpoint
also, initial support for patch models in rust!
2025-12-19 11:51:23 -05:00
asleepyskye
f22ba3f0ea fix(bot): add interaction error for wrong account
Some checks failed
Build and push Docker image / .net docker build (push) Has been cancelled
.net checks / run .net tests (push) Has been cancelled
.net checks / dotnet-format (push) Has been cancelled
2025-12-10 13:04:55 -05:00
Iris System
c4679ccfb8 chore(docs): update rolerestrict FAQ to mirror updated blueberry tag 2025-11-19 11:54:05 +13:00
Iris System
7d7442eb16 chore(docs): add Prodigi sponsor info 2025-11-19 11:53:34 +13:00
43 changed files with 3297 additions and 8056 deletions

View file

@ -183,4 +183,6 @@ public static class Errors
public static PKError ChannelNotFound(string channelString) =>
new($"Channel \"{channelString}\" not found or is not in this server.");
public static PKError InteractionWrongAccount(ulong user) => new($"This prompt is only available for <@{user}>");
}

View file

@ -5,6 +5,7 @@ using Myriad.Rest.Types.Requests;
using Myriad.Types;
using NodaTime;
using PluralKit.Core;
namespace PluralKit.Bot.Interactive;
@ -47,6 +48,11 @@ public abstract class BaseInteractive
new InteractionApplicationCommandCallbackData { Components = GetComponents() });
}
protected async Task Error(InteractionContext ctx, PKError error)
{
await ctx.Reply(content: $"{Emojis.Error} {error.Message}");
}
protected async Task Finish(InteractionContext? ctx = null)
{
foreach (var button in _buttons)

View file

@ -43,7 +43,7 @@ public class YesNoPrompt: BaseInteractive
{
if (ctx.User.Id != User)
{
await Update(ctx);
await Error(ctx, Errors.InteractionWrongAccount(User ?? 0));
return;
}

31
docs/.gitignore vendored
View file

@ -1,12 +1,23 @@
pids
logs
node_modules
npm-debug.log
coverage/
run
dist
# Output
.output
.vercel
.netlify
.wrangler
/.svelte-kit
/build
# OS
.DS_Store
.nyc_output
.basement
config.local.js
basement_dist
Thumbs.db
# Env
.env
.env.*
!.env.example
!.env.test
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

1
docs/.npmrc Normal file
View file

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

View file

@ -1,19 +0,0 @@
# PluralKit docs
The documentation is built using [Vuepress](https://vuepress.vuejs.org/). All website content is located in the `content/` subdirectory.
Most site parameters, including the sidebar layout, are defined in `content/.vuepress/config.js`. Some additional CSS is defined in `content/.vuepress/styles`.
## Building
First, install [Node.js](https://nodejs.org/en/download/) and [Yarn](https://classic.yarnpkg.com/en/). Then, run the `dev` command:
```sh
$ yarn
$ yarn dev
```
This will start a development server on http://localhost:8080/. Note that changes to the sidebar or similar generally need a full restart (Ctrl-C) to take effect, while content-only changes will hot-reload.
For a full HTML build, run `yarn build`. Files will be output in `content/.vuepress/dist` by default.
## Deployment
The docs are deployed using [Netlify](https://www.netlify.com/) with CI.

View file

@ -467,7 +467,7 @@ Features:
Bugfixes:
- fixed importing pronouns and message count
- fixed looking up messages with a discord canary link (and then fixed looking up normal links >.<)
- fixed looking up messages with a discord canary link (and then fixed looking up normal links >.\<)
- fixed a few "internal error" messages and other miscellaneous bugs
(also, `pk;member <name> soulscream` is a semi-secret command for the time being, if you know what this means, have fun :3 🍬)

View file

@ -55,8 +55,8 @@ This includes:
For more information about why certain information must be public, see [this github issue](https://github.com/PluralKit/PluralKit/issues/238).
### Is there a way to restrict PluralKit usage to a certain role? / Can I remove PluralKit access for specific users in my server?
This is not a feature currently available in PluralKit. It may be added in the future.
In the meantime, this feature is supported in Tupperbox (an alternative proxying bot) - ask about it in their support server: <https://discord.gg/Z4BHccHhy3>
PluralKit does not, and *will not*, support restricting usage of the bot by role.
This feature is supported in Tupperbox (an alternative proxying bot) - ask about it in their support server: <https://discord.gg/Z4BHccHhy3>
### Is it possible to block proxied messages (like blocking a user)?
No. Since proxied messages are posted through webhooks, and those technically aren't real users on Discord's end, it's not possible to block them. Blocking PluralKit itself will also not block the webhook messages. Discord also does not allow you to control who can receive a specific message, so it's not possible to integrate a blocking system in the bot, either. Sorry :/

View file

@ -11,4 +11,14 @@ This bot detects messages with certain tags associated with a profile, then repl
#### for example...
![demonstration of PluralKit](./assets/demo.gif)
For more information, see the links to the left, or click [here](https://discord.com/oauth2/authorize?client_id=466378653216014359&scope=bot%20applications.commands&permissions=536995904) to invite the bot to your server!
For more information, see the links to the left, or click [here](https://discord.com/oauth2/authorize?client_id=466378653216014359&scope=bot%20applications.commands&permissions=536995904) to invite the bot to your server!
## Sponsors
<div style="text-align:center;">
![Prodigi logo](./assets/prodigi.png)
</div>
PluralKit's infrastructure is generously sponsored by [Prodigi](https://prodigi.nz), a New Zealand based technology services provider.

View file

@ -1,3 +1,3 @@
app = "pluralkit-docs"
primary_region = "arn"
primary_region = "sjc"
http_service.internal_port = 8000

View file

@ -1,20 +1,38 @@
{
"name": "pluralkit-docs",
"private": true,
"description": "Documentation for PluralKit",
"scripts": {
"dev": "vuepress dev content",
"build": "vuepress build content"
},
"license": "AGPL-3.0-or-later",
"devDependencies": {
"@vuepress/plugin-back-to-top": "1.8.2",
"markdown-it-custom-header-link": "^1.0.5",
"vuepress": "1.8.2",
"vuepress-plugin-clean-urls": "1.1.2",
"vuepress-plugin-dehydrate": "1.1.5"
},
"dependencies": {
"vuepress-theme-default-prefers-color-scheme": "2.0.0"
}
"name": "docs",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
},
"devDependencies": {
"@sveltejs/adapter-node": "^5.4.0",
"@sveltejs/kit": "^2.49.1",
"@sveltejs/vite-plugin-svelte": "^6.2.1",
"@tabler/icons-svelte": "^3.36.0",
"@tailwindcss/postcss": "^4.1.18",
"@tailwindcss/typography": "^0.5.13",
"@tailwindcss/vite": "^4.1.17",
"@types/node": "^25.0.3",
"@types/nprogress": "^0.2.3",
"autoprefixer": "^10.4.23",
"daisyui": "^4.12.24",
"nprogress": "^0.2.0",
"postcss": "^8.5.3",
"sass": "^1.77.8",
"svelte": "^5.45.6",
"svelte-check": "^4.3.4",
"tailwindcss": "^3.4.17",
"typescript": "^5.9.3",
"vite": "^7.2.6"
},
"dependencies": {
"mdsvex": "^0.12.6"
}
}

2356
docs/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load diff

6
docs/postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

13
docs/src/app.d.ts vendored Normal file
View file

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

11
docs/src/app.html Normal file
View file

@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<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>

View file

@ -0,0 +1,27 @@
<script lang="ts">
import { env } from "$env/dynamic/public"
// @ts-ignore
const version = __COMMIT_HASH__.slice(1, __COMMIT_HASH__.length - 1)
</script>
<footer class="footer items-center p-4">
<nav class="grid-flow-col gap-4">
<span
>Commit: <a
aria-label="View commit on github"
class="underline"
href={`${
env.PUBLIC_REPOSITORY_URL
? env.PUBLIC_REPOSITORY_URL
: "https://github.com/Draconizations/pk-dashboard-sveltekit"
}/commit/${version}`}>{version}</a
>
</span>
</nav>
<nav class="grid-flow-col gap-4 md:place-self-center md:justify-self-end">
<a class="link-hover" href="/about">About</a>
<a class="link-hover" href="/privacy">Privacy</a>
<a class="link-hover" href="/changelog">Changelog</a>
</nav>
</footer>

View file

@ -0,0 +1,139 @@
<script lang="ts">
import {
IconMenu2,
IconBook,
IconBrandDiscord,
IconShare3,
IconUsers,
IconBoxMultiple,
IconAdjustments,
IconPaint,
IconLogout,
IconAddressBook,
IconHome,
IconSettings,
IconStatusChange,
IconInfoCircle,
IconDashboard,
IconLayoutDashboard,
} from "@tabler/icons-svelte"
let userMenu: HTMLDetailsElement
let navbarMenu: HTMLDetailsElement
</script>
<div class="navbar bg-base-100">
<div class="navbar-start flex-1">
<details class="dropdown" bind:this={navbarMenu}>
<summary class="btn btn-ghost md:hidden">
<IconMenu2 />
</summary>
<ul class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
<li>
<a href="/" onclick={() => (navbarMenu.open = false)}>
<IconHome /> Homepage
</a>
</li>
<li>
<a
href="https://discord.com/oauth2/authorize?client_id=466378653216014359&scope=bot%20applications.commands&permissions=536995904"
onclick={() => (navbarMenu.open = false)}
>
<IconShare3 /> Invite bot
</a>
</li>
<li>
<a href="https://pluralkit.me/" onclick={() => (navbarMenu.open = false)}
><IconBook /> Documentation</a
>
</li>
<li>
<a href="https://discord.gg/PczBt78" onclick={() => (navbarMenu.open = false)}
><IconBrandDiscord /> Support server</a
>
</li>
</ul>
</details>
<a href="/" class="hidden text-xl btn btn-ghost md:inline-flex">PluralKit</a>
</div>
<div class="hidden navbar-center md:flex">
<ul class="px-1 menu menu-horizontal">
<li><a href="https://dash.pluralkit.me/"><IconLayoutDashboard /> Web dashboard</a></li>
<li><a href="https://discord.gg/PczBt78"><IconBrandDiscord /> Support server</a></li>
<li>
<a
href="https://discord.com/oauth2/authorize?client_id=466378653216014359&scope=bot%20applications.commands&permissions=536995904"
>
<IconShare3 /> Invite bot
</a>
</li>
<li><a href="https://status.pluralkit.me"><IconInfoCircle /> Status</a></li>
</ul>
</div>
<div class="navbar-end w-auto">
<a href="/settings#theme" class="mr-4 tooltip tooltip-bottom" data-tip="Change theme"
><IconPaint /></a
>
{#if false /*dash.user*/}
<details class="dropdown dropdown-left" bind:this={userMenu}>
<summary class="mr-2 list-none">
{#if false /*dash.user.avatar_url*/}
<div class="avatar">
<div class="w-12 rounded-full">
<!-- <img alt="your system avatar" src={dash.user.avatar_url} /> -->
</div>
</div>
{:else}
<div class="avatar">
<div class="w-12 rounded-full">
<img alt="An icon of myriad" src="/myriad_write.png" />
</div>
</div>
{/if}
</summary>
<ul
data-sveltekit-preload-data="tap"
class="menu menu-sm menu-dropdown dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-36"
>
<!-- <li>
<a href={`/dash/${dash.user?.id}?tab=overview`} onclick={() => (userMenu.open = false)}
><IconAdjustments /> Overview</a
>
</li>
<li>
<a href={`/dash/${dash.user?.id}?tab=system`} onclick={() => (userMenu.open = false)}
><IconAddressBook /> System</a
>
</li>
<li>
<a href={`/dash/${dash.user?.id}?tab=members`} onclick={() => (userMenu.open = false)}
><IconUsers /> Members</a
>
</li>
<li>
<a href={`/dash/${dash.user?.id}?tab=groups`} onclick={() => (userMenu.open = false)}
><IconBoxMultiple /> Groups</a
>
</li> -->
<hr class="my-2" />
<li>
<a href="/settings/general" onclick={() => (userMenu.open = false)}
><IconSettings /> Settings</a
>
</li>
<li>
<form method="post" action="/?/logout">
<IconLogout />
<input
onclick={() => (userMenu.open = false)}
class="text-error w-min"
type="submit"
value="Logout"
/>
</form>
</li>
</ul>
</details>
{/if}
</div>
</div>

View file

@ -0,0 +1,107 @@
<script lang="ts">
import { page } from "$app/stores";
const mdModules = import.meta.glob('/content/**/*.md', { eager: true }) as Record<string, { metadata?: { title?: string; permalink?: string } }>;
const pathToTitle: Record<string, string> = {};
for (const [filePath, mod] of Object.entries(mdModules)) {
const urlPath = filePath
.replace('/content', '')
.replace(/\/index\.md$/, '')
.replace(/\.md$/, '');
if (mod.metadata?.title) {
pathToTitle[urlPath || '/'] = mod.metadata.title;
}
}
function getTitle(path: string): string {
return pathToTitle[path] || path.split('/').pop() || path;
}
const sidebar = [
{
title: "Home",
href: "/",
},
{
title: "Add to your server",
href: "https://discord.com/oauth2/authorize?client_id=466378653216014359&scope=bot%20applications.commands&permissions=536995904",
},
{
title: "Updates",
sidebarDepth: 1,
children: [
"/posts",
"/changelog",
]
},
{
title: "Documentation",
sidebarDepth: 2,
children: [
"/getting-started",
"/user-guide",
"/command-list",
"/privacy-policy",
"/terms-of-service",
"/faq",
"/tips-and-tricks"
]
},
{
title: "For server staff",
children: [
"/staff/permissions",
"/staff/moderation",
"/staff/disabling",
"/staff/logging",
"/staff/compatibility",
]
},
{
title: "API Documentation",
children: [
"/api/changelog",
"/api/reference",
"/api/endpoints",
"/api/models",
"/api/errors",
"/api/dispatch"
]
},
{
title: "Join the support server",
href: "https://discord.gg/PczBt78",
},
];
function isActive(href: string): boolean {
return $page.url.pathname === href;
}
</script>
<aside class="w-80 bg-base-200 p-4 overflow-y-auto shrink-0 min-h-0">
<ul class="menu w-full">
{#each sidebar as item}
{#if item.children}
<li class="menu-title flex flex-row items-center gap-2 mt-4">
{item.title}
</li>
{#each item.children as child}
<li>
<a href={child} class:active={isActive(child)}>
{getTitle(child)}
</a>
</li>
{/each}
{:else}
<li>
<a href={item.href} class:active={isActive(item.href)}>
{item.title}
</a>
</li>
{/if}
{/each}
</ul>
</aside>

212
docs/src/lib/app.scss Normal file
View file

@ -0,0 +1,212 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
html {
height: 100%;
}
@layer components {
hr {
@apply border-muted/50;
}
.btn-menu {
@apply px-4 py-2 h-auto min-h-0 justify-start;
}
.box {
@apply rounded-xl bg-base-200 p-4;
}
.menu :where(li:not(.menu-title) > :not(ul):not(details):not(.menu-title)),
.menu :where(li:not(.menu-title) > details > summary:not(.menu-title)) {
@apply select-text;
}
.tabs-lifted.tabs-box > .tab.tab-active:not(.tab-disabled):not([disabled]) {
@apply bg-base-200;
}
.tabs-lifted.tabs-box .tab.tab-active:not(.tab-disabled):not([disabled])::before,
.tabs-lifted.tabs-box .tab.tab-active:not(.tab-disabled):not([disabled]):first-child::before,
.tabs-lifted.tabs-box .tab.tab-active:not(.tab-disabled):not([disabled]):last-child::before {
background-image: none;
}
/* start of discord markdown styling */
.discord-markdown {
blockquote {
@apply pl-3 border-l-4 border-muted/50;
}
ul {
@apply list-disc pl-4;
}
ol {
@apply list-decimal pl-4;
}
.d-emoji {
@apply h-4 w-auto inline;
}
.d-spoiler {
@apply bg-base-content text-base-content;
border-radius: 4px;
transition-delay: 6000s;
&::selection {
@apply text-base-content;
background-color: transparent;
}
&:active {
@apply bg-base-300;
transition-delay: 0s;
}
}
code {
@apply px-1 text-sm rounded-sm bg-base-200;
}
pre > code {
@apply py-1 px-2 md:px-3 md:py-2 rounded-xl;
}
a {
@apply link-primary;
}
small {
@apply block text-muted;
}
}
/* end of discord markdown styling */
/* button styling! */
.btn {
@apply font-normal;
}
/* daisyUI applies some styling to lists in .menu that we don't want */
/* so we reset them here */
:where(.menu li),
:where(.menu ul) {
position: static;
}
.discord-markdown ul {
position: static;
white-space: normal;
margin-inline-start: 0;
margin-inline-end: 0;
}
.discord-markdown li {
position: static;
display: list-item;
}
.menu .discord-markdown :where(li:not(.menu-title) > :not(ul, details, .menu-title, .btn)),
.menu .discord-markdown :where(li:not(.menu-title) > details > summary:not(.menu-title)) {
display: unset;
padding: unset;
}
/* end of the .menu reset */
}
[data-theme="dark"],
[data-theme="light"],
[data-theme="acid"],
[data-theme="cotton"],
[data-theme="autumn"],
[data-theme="coffee"] {
--sv-min-height: 40px;
--sv-bg: var(--fallback-b1, oklch(var(--b1) / var(--tw-bg-opacity)));
--sv-disabled-bg: var(--fallback-b3, oklch(var(--b3) / var(--tw-bg-opacity)));
--sv-border: 1px solid oklch(var(--muted) / 0.5);
--sv-border-radius: 6px;
--sv-general-padding: 0.25rem;
--sv-control-bg: var(--sv-bg);
--sv-item-wrap-padding: 3px 3px 3px 6px;
--sv-item-selected-bg: var(--fallback-b3, oklch(var(--b3) / var(--tw-bg-opacity)));
--sv-item-btn-color: var(--fallback-bc, oklch(var(--bc) / 1));
--sv-item-btn-color-hover: var(
--fallback-bc,
oklch(var(--bc) / 0.6)
); /* same as icon-color-hover in default theme */
--sv-item-btn-bg: transparent;
--sv-item-btn-bg-hover: transparent;
--sv-icon-color: var(--sv-item-btn-color);
--sv-icon-color-hover: var(--sv-item-btn-color-hover);
--sv-icon-bg: transparent;
--sv-icon-size: 20px;
--sv-separator-bg: transparent;
--sv-btn-border: 0;
--sv-placeholder-color: transparent;
--sv-dropdown-bg: var(--sv-bg);
--sv-dropdown-offset: 1px;
--sv-dropdown-border: 1px solid oklch(var(--muted) / 0.5);
--sv-dropdown-width: auto;
--sv-dropdown-shadow: none;
--sv-dropdown-height: 320px;
--sv-dropdown-active-bg: var(--fallback-b3, oklch(var(--b3) / var(--tw-bg-opacity)));
--sv-dropdown-selected-bg: oklch(var(--p) / 0.2);
--sv-create-kbd-border: none;
--sv-create-kbd-bg: transparent;
--sv-create-disabled-bg: transparent;
--sv-loader-border: none;
--sv-item-wrap-padding: 0.375rem 0.25rem;
}
.join-item.svelecte-control-pk {
--sv-min-height: 2rem;
.sv-control {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
.group-control {
--sv-dropdown-active-bg: transparent;
--sv-item-wrap-padding: 0.25rem 0;
}
.group-control .option {
width: calc(100% + 0.5rem);
}
.sv-item--wrap {
border-radius: 4px;
padding: 0.25rem;
font-size: 14px;
}
.sv-item--wrap.in-dropdown {
padding: 0;
position: relative;
}
.sv-item--wrap.in-dropdown:not(:last-child)::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 0;
border-bottom: 1px solid oklch(var(--muted) / 0.5);
}
.sv-dropdown-scroll {
padding: 0 0.75rem !important;
}
.svelecte {
flex: auto !important;
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

1
docs/src/lib/index.ts Normal file
View file

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

View file

@ -0,0 +1,77 @@
#themed-container {
--nprogress-color: var(--fallback-p, oklch(var(--p) / 1));
}
/* Make clicks pass-through */
#nprogress {
pointer-events: none;
.bar {
background: var(--nprogress-color);
position: fixed;
z-index: 1031;
top: 0;
left: 0;
width: 100%;
height: 4px;
}
}
/* Fancy blur effect */
/* #nprogress .peg {
display: block;
position: absolute;
right: 0px;
width: 100px;
height: 100%;
box-shadow: 0 0 10px var(--nprogress-color), 0 0 5px var(--nprogress-color);
opacity: 1.0;
-webkit-transform: rotate(3deg) translate(0px, -4px);
-ms-transform: rotate(3deg) translate(0px, -4px);
transform: rotate(3deg) translate(0px, -4px);
} */
/* Remove these to get rid of the spinner */
/* #nprogress .spinner {
display: block;
position: fixed;
z-index: 1031;
top: 15px;
right: 15px;
}
#nprogress .spinner-icon {
width: 18px;
height: 18px;
box-sizing: border-box;
border: solid 2px transparent;
border-top-color: var(--nprogress-color);
border-left-color: var(--nprogress-color);
border-radius: 50%;
-webkit-animation: nprogress-spinner 400ms linear infinite;
animation: nprogress-spinner 400ms linear infinite;
}
.nprogress-custom-parent {
overflow: hidden;
position: relative;
}
.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar {
position: absolute;
}
@-webkit-keyframes nprogress-spinner {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes nprogress-spinner {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
} */

View file

@ -0,0 +1,64 @@
<script lang="ts">
import { browser } from "$app/environment"
import NavBar from "$components/NavBar.svelte"
import Sidebar from "$components/Sidebar.svelte"
import "$lib/app.scss"
import "$lib/nprogress.scss"
import type { LayoutData } from "./$types"
import Footer from "$components/Footer.svelte"
import { page } from "$app/stores"
import { navigating } from "$app/stores"
import nprogress from "nprogress"
// import apiClient from "$api"
export let data: LayoutData
// if (browser) {
// window.api = apiClient(fetch, data.apiBaseUrl)
// }
if (data.token && browser) {
localStorage.setItem("pk-token", data.token)
} else if (browser) {
localStorage.removeItem("pk-token")
}
nprogress.configure({
parent: "#themed-container",
})
$: {
if ($navigating) nprogress.start()
else if (!$navigating) nprogress.done()
}
// dash.initUser(data.system)
</script>
<div
id="themed-container"
class="max-w-screen h-screen bg-base-100 flex flex-col"
data-theme="coffee"
>
<NavBar />
<div class="flex flex-row flex-1 min-h-0">
<Sidebar />
<main class="flex-1 overflow-y-auto min-h-0">
<slot />
</main>
</div>
<Footer />
</div>
<svelte:head>
<title>PluralKit | {$page.data?.meta?.title ?? "Home"}</title>
<meta
property="og:title"
content={`PluralKit | ${$page.data?.meta?.ogTitle ?? "Web Dashboard"}`}
/>
<meta property="theme-color" content={`#${$page.data?.meta?.color ?? "da9317"}`} />
<meta
property="og:description"
content={$page.data?.meta?.ogDescription ?? "PluralKit's official dashboard."}
/>
</svelte:head>

View file

@ -0,0 +1,7 @@
<script>
let { data } = $props();
</script>
<div class="max-w-full bg-base-200 prose">
<div class="m-5" style="max-width: 900px"><data.PageContent /></div>
</div>

View file

@ -0,0 +1,16 @@
import { error } from '@sveltejs/kit';
const pages = import.meta.glob('/content/**/*.md', { eager: true }) as Record<string, { default: unknown }>;
export async function load({ params }) {
const slug = params.slug || 'index';
const page = pages[`/content/${slug}.md`] || pages[`/content/${slug}/index.md`];
if (!page) {
throw error(404, `Page not found: ${slug}`);
}
return {
PageContent: page.default
};
}

View file

@ -0,0 +1 @@
@import 'tailwindcss';

View file

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 664 KiB

After

Width:  |  Height:  |  Size: 664 KiB

Before After
Before After

BIN
docs/static/assets/prodigi.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Before After
Before After

26
docs/svelte.config.js Normal file
View file

@ -0,0 +1,26 @@
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import { mdsvex } from 'mdsvex';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://svelte.dev/docs/kit/integrations
// for more information about preprocessors
preprocess: [
mdsvex({
extensions: [".md"]
}),
vitePreprocess(),
],
extensions: [".svelte", ".md"],
kit: {
adapter: adapter(),
alias: {
$components: "src/components",
$lib: "src/lib",
}
}
};
export default config;

113
docs/tailwind.config.js Normal file
View file

@ -0,0 +1,113 @@
import daisyui from "daisyui"
import typography from "@tailwindcss/typography"
import { light, dark, night, autumn, coffee, halloween, pastel } from "daisyui/src/theming/themes"
/** @type {import('tailwindcss').Config} */
export default {
content: ["./src/**/*.{html,js,svelte,ts}"],
plugins: [typography, daisyui],
theme: {
extend: {
colors: {
muted: "oklch(var(--muted) / <alpha-value>)",
},
},
},
daisyui: {
themes: [
{
// cool light
light: {
...light,
primary: "#da9317",
secondary: "#9e66ff",
accent: "#22ded8",
// DARK BUTTONS
/*
"--muted": "69.38% 0.01 252.85",
"base-200": "#ededed",
"base-300": "#e1e3e3",
"primary-content": "#090101",
"neutral-content": "#f9fcff",
"base-content": "10161e",
"secondary-content": "fafafa"
*/
// LIGHT BUTTONS
"--muted": "59.37% 0.01 252.85",
"base-200": "#ededed",
"base-300": "#e5e7e7",
"primary-content": "#090101",
neutral: "#f8f8f8",
"neutral-content": "#040507",
"base-content": "10161e",
"secondary-content": "#090101",
},
// cool dark
dark: {
...dark,
primary: "#da9317",
secondary: "#ae81fc",
accent: "#6df1fc",
"--muted": "59.37% 0.0117 254.07",
"base-100": "#22262b",
"base-200": "#191c1f",
"base-300": "#17191b",
"base-content": "#ced3dc",
neutral: "#33383e",
"neutral-content": "#e1e2e3",
},
// bright dark
acid: {
...night,
primary: "#49c701",
secondary: "#00c6cf",
accent: "#f29838",
"base-100": "#1a2433",
"base-200": "#101a27",
"base-300": "#111724",
neutral: "#242e41",
"--muted": "60.8% 0.05 272",
},
// bright light (trans rights!)
cotton: {
...pastel,
primary: "#ff69a8",
secondary: "#63a7f9",
accent: "#f8b939",
neutral: "#f8f8f8",
"base-200": "#eeecf1",
"base-300": "#e2e1e7",
"--muted": "59% 0.01 252.85",
"--rounded-btn": "0.5rem",
},
// warm light
autumn: {
...autumn,
primary: "#e38010",
success: "#2c7866",
"success-content": "#eeeeee",
error: "#97071a",
"error-content": "#eeeeee",
neutral: "#ebebeb",
"neutral-content": "#141414",
"base-100": "#fcfcfc",
"--muted": "67.94% 0.01 39.18",
},
// warm dark
coffee: {
...halloween,
secondary: "#bc4b2b",
accent: coffee.accent,
primary: coffee.primary,
info: "#3499c0",
neutral: "#120f12",
"neutral-content": "#dfe0de",
"base-200": "#1a1a1a",
"base-300": "#181818",
"base-content": "#d9dbd8",
"--muted": "57.65% 0 54",
},
},
],
},
}

20
docs/tsconfig.json Normal file
View file

@ -0,0 +1,20 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"rewriteRelativeImportExtensions": true,
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
//
// To make changes to top-level options such as include and exclude, we recommend extending
// the generated config; see https://svelte.dev/docs/kit/configuration#typescript
}

18
docs/vite.config.ts Normal file
View file

@ -0,0 +1,18 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { mdsvex } from 'mdsvex';
import { defineConfig } from 'vite';
import { execSync } from "node:child_process"
const hash = execSync("git rev-parse --short HEAD").toString().trim()
export default defineConfig({
plugins: [sveltekit(), mdsvex({ extension: ".md" })],
server: {
fs: {
allow: ["."]
}
},
define: {
__COMMIT_HASH__: JSON.stringify("_" + hash),
},
})

File diff suppressed because it is too large Load diff