Skip to content

Commit

Permalink
Dark mode
Browse files Browse the repository at this point in the history
  • Loading branch information
matvp91 committed Oct 18, 2024
1 parent f3b1e86 commit 0931c02
Show file tree
Hide file tree
Showing 20 changed files with 605 additions and 82 deletions.
2 changes: 1 addition & 1 deletion packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"./client": "./src/client.ts"
},
"scripts": {
"dev": "bun run ./src/index.ts --watch",
"dev": "bun --watch ./src/index.ts",
"build": "bun build ./src/index.ts --target=bun --outdir=./dist",
"lint": "eslint \"./src/**/*.ts\" && prettier --check \"./src/**/*.ts\"",
"typecheck": "tsc"
Expand Down
33 changes: 4 additions & 29 deletions packages/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Elysia, t } from "elysia";
import { cors } from "@elysiajs/cors";
import { swagger } from "@elysiajs/swagger";
import { swagger, onAfterHandle } from "./swagger";
import { addTranscodeJob, addPackageJob } from "@mixwave/artisan/producer";
import {
LangCodeSchema,
Expand All @@ -14,36 +14,9 @@ import { StorageFolderSchema, StorageFileSchema, JobSchema } from "./types";

export type App = typeof app;

const CUSTOM_SCALAR_CSS = `
.scalar-container.z-overlay {
padding-left: 16px;
padding-right: 16px;
}
.scalar-api-client__send-request-button, .show-api-client-button {
background: var(--scalar-button-1);
}
`;

const app = new Elysia()
.use(cors())
.use(
swagger({
documentation: {
info: {
title: "Mixwave API",
description:
"The Mixwave API is organized around REST, returns JSON-encoded responses " +
"and uses standard HTTP response codes and verbs.",
version: "1.0.0",
},
},
scalarConfig: {
hideDownloadButton: true,
customCss: CUSTOM_SCALAR_CSS,
},
}),
)
.use(swagger)
.model({
LangCode: LangCodeSchema,
VideoCodec: VideoCodecSchema,
Expand Down Expand Up @@ -287,6 +260,8 @@ const app = new Elysia()
},
);

app.onAfterHandle(onAfterHandle);

app.on("stop", () => {
process.exit(0);
});
Expand Down
71 changes: 71 additions & 0 deletions packages/api/src/swagger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { swagger as elysiaSwagger } from "@elysiajs/swagger";

const CUSTOM_SCALAR_CSS = `
.scalar-container.z-overlay {
padding-left: 16px;
padding-right: 16px;
}
.scalar-api-client__send-request-button, .show-api-client-button {
background: var(--scalar-button-1);
}
`;

export const swagger = elysiaSwagger({
documentation: {
info: {
title: "Mixwave API",
description:
"The Mixwave API is organized around REST, returns JSON-encoded responses " +
"and uses standard HTTP response codes and verbs.",
version: "1.0.0",
},
},
scalarConfig: {
hideDownloadButton: true,
customCss: CUSTOM_SCALAR_CSS,
},
});

const scalarScript = `
<script>
const searchParams = new URLSearchParams(window.location.search);
const theme = searchParams.get("theme");
const isDark = localStorage.getItem("isDark") === "true";
if (theme === "dark" && !isDark) {
localStorage.setItem("isDark", "true");
location.reload();
} else if (theme !== "dark" && isDark) {
localStorage.setItem("isDark", "false");
location.reload();
}
</script>
`;

export async function onAfterHandle({
request,
response,
}: {
request: Request;
response: Response;
}) {
const url = new URL(request.url);

if (url.pathname.endsWith("/swagger")) {
const text = await response.text();
const lines = text.split("\n");
lines.splice(
lines.findIndex((line) => line.trim() === "<body>"),
0,
scalarScript,
);
return new Response(lines.join(""), {
headers: {
"content-type": "text/html; charset=utf8",
},
});
}

return response;
}
1 change: 1 addition & 0 deletions packages/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@radix-ui/react-checkbox": "^1.1.1",
"@radix-ui/react-collapsible": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-radio-group": "^1.2.0",
Expand Down
15 changes: 9 additions & 6 deletions packages/dashboard/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Suspense } from "react";
import { Toaster } from "@/components/ui/toaster";
import { PlayerPage } from "./pages/PlayerPage";
import { StoragePage } from "./pages/StoragePage";
import { ThemeProvider } from "@/components/ui/theme-provider";

const queryClient = new QueryClient();

Expand Down Expand Up @@ -50,11 +51,13 @@ const router = createBrowserRouter([

export function App() {
return (
<QueryClientProvider client={queryClient}>
<Suspense>
<RouterProvider router={router} />
</Suspense>
<Toaster />
</QueryClientProvider>
<ThemeProvider defaultTheme="light" storageKey="vite-ui-theme">
<QueryClientProvider client={queryClient}>
<Suspense>
<RouterProvider router={router} />
</Suspense>
<Toaster />
</QueryClientProvider>
</ThemeProvider>
);
}
62 changes: 45 additions & 17 deletions packages/dashboard/src/components/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import MonacoEditor from "@monaco-editor/react";
import type { BeforeMount, OnChange, OnMount } from "@monaco-editor/react";
import { useEffect, useState } from "react";
import { useTheme } from "@/components/ui/theme-provider";
import type { BeforeMount, OnChange, OnMount } from "@monaco-editor/react";

type EditorProps = {
schema: object;
Expand All @@ -15,6 +16,9 @@ export function Editor({
onSave,
localStorageKey,
}: EditorProps) {
const { theme } = useTheme();
const style = useMonacoStyle();

const [defaultValue] = useState(() => {
const localStorageValue = localStorageKey
? localStorage.getItem(localStorageKey)
Expand Down Expand Up @@ -53,23 +57,47 @@ export function Editor({
};

return (
<div className="h-full flex flex-col bg-[#1e1e1e]">
<div className="p-4 flex gap-2">
<div className="text-white flex items-center">{title}</div>
<div className="h-full flex flex-col">
<div
className="border-b flex px-4"
style={{ height: "calc(3.5rem + 4px)" }}
>
<div className="flex items-center">{title}</div>
</div>
<div className="h-full relative">
{style}
<MonacoEditor
className="absolute inset-0"
defaultLanguage="json"
defaultValue={defaultValue}
beforeMount={beforeMount}
onMount={onMount}
onChange={onChange}
defaultPath="custom"
theme={theme === "dark" ? "vs-dark" : "light"}
options={{
minimap: {
enabled: false,
},
tabSize: 2,
}}
/>
</div>
<MonacoEditor
className="h-full"
defaultLanguage="json"
defaultValue={defaultValue}
beforeMount={beforeMount}
onMount={onMount}
onChange={onChange}
defaultPath="custom"
theme="vs-dark"
options={{
tabSize: 2,
}}
/>
</div>
);
}

function useMonacoStyle() {
const { theme } = useTheme();

if (theme === "dark") {
return (
<style>{`
.monaco-editor, .monaco-editor-background { background-color: inherit; }
.monaco-editor .margin { background-color: inherit; }
`}</style>
);
}

return null;
}
23 changes: 18 additions & 5 deletions packages/dashboard/src/components/JobState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,31 @@ import type { Job } from "@/api";

export function JobState({ state }: { state: Job["state"] }) {
if (state === "completed") {
return createCircle("bg-emerald-200 text-emerald-800", Check);
return createCircle(
"bg-emerald-200 text-emerald-800 dark:bg-emerald-400",
Check,
);
}
if (state === "failed") {
return createCircle("bg-red-200 text-red-800", X);
return createCircle("bg-red-200 text-red-800 dark:bg-red-400", X);
}
if (state === "running") {
return createCircle("bg-blue-200 text-blue-800", Loader, "animate-spin");
return createCircle(
"bg-blue-200 text-blue-800 dark:bg-blue-400",
Loader,
"animate-spin",
);
}
if (state === "skipped") {
return createCircle("bg-gray-200 text-gray-800", CircleOff);
return createCircle(
"bg-gray-200 text-gray-800 dark:bg-gray-400",
CircleOff,
);
}
return createCircle("bg-violet-200 text-violet-800", CircleDotDashed);
return createCircle(
"bg-violet-200 text-violet-800 dark:bg-gray-400",
CircleDotDashed,
);
}

function createCircle(
Expand Down
2 changes: 1 addition & 1 deletion packages/dashboard/src/components/JobsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function JobsList({ jobs }: JobsListProps) {
<li key={job.id} className="mb-2">
<Link
to={`/jobs/${job.id}`}
className="px-4 h-20 flex items-center border border-border rounded-md bg-white hover:shadow-sm transition-shadow hover:border-gray-300"
className="px-4 h-20 flex items-center border border-border rounded-md hover:shadow-sm transition-shadow hover:bg-muted/50"
>
<div className="grow grid grid-cols-3 gap-2 items-center">
<div className="flex items-center gap-4">
Expand Down
10 changes: 5 additions & 5 deletions packages/dashboard/src/components/JsonHighlight.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { useMemo } from "react";
import { Light as SyntaxHighlighter } from "react-syntax-highlighter";
import json from "react-syntax-highlighter/dist/esm/languages/hljs/json";
import style from "react-syntax-highlighter/dist/esm/styles/hljs/stackoverflow-light";

style["hljs"].padding = "1rem";
delete style["hljs"].background;
import { useTheme } from "@/components/ui/theme-provider";
import { styleLight, styleDark } from "@/lib/syntax-styles";

SyntaxHighlighter.registerLanguage("json", json);

Expand All @@ -13,6 +11,8 @@ type SyntaxHighlightProps = {
};

export function JsonHighlight({ json }: SyntaxHighlightProps) {
const { theme } = useTheme();

const data = useMemo(() => {
const parsed = JSON.parse(json);
return JSON.stringify(parsed, null, 2);
Expand All @@ -21,7 +21,7 @@ export function JsonHighlight({ json }: SyntaxHighlightProps) {
return (
<SyntaxHighlighter
className="rounded-md text-xs border border-border"
style={style}
style={theme === "dark" ? styleDark : styleLight}
language="json"
>
{data}
Expand Down
8 changes: 4 additions & 4 deletions packages/dashboard/src/components/PlayerAccordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ export function PlayerAccordion({
<AccordionItem value="session">
<AccordionTrigger className="px-2">Session</AccordionTrigger>
<AccordionContent>
<div className="p-4 bg-gray-50">
<div className="px-4">
<Label>Master playlist URL</Label>
<div className="mt-2 flex gap-2">
<Input
className="text-gray-700"
className="text-muted-foreground"
value={masterUrl ?? ""}
onClick={(event) => {
(event.target as HTMLInputElement).select();
Expand Down Expand Up @@ -75,7 +75,7 @@ export function PlayerAccordion({
<AccordionItem value="settings">
<AccordionTrigger className="px-2">Settings</AccordionTrigger>
<AccordionContent className="space-y-4">
<div className="p-4 rounded-md bg-gray-50 space-y-2">
<div className="px-4 rounded-md space-y-2">
<Label>Language</Label>
<SelectObject
className="w-full max-w-full"
Expand All @@ -98,7 +98,7 @@ export function PlayerAccordion({
</span>{" "}
component.
</p>
<div className="p-4 rounded-md bg-gray-50">
<div className="px-4 rounded-md">
<PlayerMetadataForm values={metadata} onSubmit={setMetadata} />
</div>
</AccordionContent>
Expand Down
6 changes: 3 additions & 3 deletions packages/dashboard/src/components/PlayerNpmInstall.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ export function PlayerNpmInstall() {
return (
<div className="flex py-2 pl-4 pr-4 border border-border rounded-md items-center">
<svg
className="h-[10px]"
className="h-[10px] fill-primary/80"
viewBox="0 0 12.32 9.33"
fill="rgba(0, 0, 0, 0.5)"
fill="currentColor"
>
<g>
<line x1="7.6" y1="8.9" x2="7.6" y2="6.9" />
Expand All @@ -18,7 +18,7 @@ export function PlayerNpmInstall() {
</g>
</svg>
<span
className="text-black/80 text-[13px] ml-1"
className="text-primary/80 text-[13px] ml-1"
style={{
fontFamily: "Consolas, monaco, monospace",
lineHeight: "24px",
Expand Down
2 changes: 1 addition & 1 deletion packages/dashboard/src/components/PlayerView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function PlayerView({ masterUrl }: PlayerViewProps) {

return (
<div className="h-full flex flex-col">
<div className="px-4 mt-4">
<div className="px-4 mt-2">
<PlayerNpmInstall />
</div>
<div className="px-4 mt-2">
Expand Down
Loading

0 comments on commit 0931c02

Please sign in to comment.