diff --git a/app/build.ts b/app/build.ts index db8dae3..0366321 100644 --- a/app/build.ts +++ b/app/build.ts @@ -1,4 +1,7 @@ +import fs from "node:fs"; + import { $ } from "bun"; +import { replace } from "esbuild-plugin-replace"; import * as tsup from "tsup"; const args = process.argv.slice(2); @@ -80,7 +83,8 @@ await tsup.build({ "src/ui/index.ts", "src/ui/client/index.ts", "src/ui/elements/index.ts", - "src/ui/main.css" + "src/ui/main.css", + "src/ui/styles.css" ], outDir: "dist/ui", external: [ @@ -108,6 +112,50 @@ await tsup.build({ } }); +/** + * Building UI Elements + * - tailwind-merge is mocked, no exclude + * - ui/client is external, and after built replaced with "bknd/client" + */ +await tsup.build({ + minify, + sourcemap, + watch, + entry: ["src/ui/elements/index.ts"], + outDir: "dist/ui/elements", + external: [ + "ui/client", + "react", + "react-dom", + "react/jsx-runtime", + "react/jsx-dev-runtime", + "use-sync-external-store" + ], + metafile: true, + platform: "browser", + format: ["esm"], + splitting: false, + bundle: true, + treeshake: true, + loader: { + ".svg": "dataurl" + }, + esbuildOptions: (options) => { + options.alias = { + // not important for elements, mock to reduce bundle + "tailwind-merge": "./src/ui/elements/mocks/tailwind-merge.ts" + }; + }, + onSuccess: async () => { + // manually replace ui/client with bknd/client + const path = "./dist/ui/elements/index.js"; + const bundle = await Bun.file(path).text(); + await Bun.write(path, bundle.replaceAll("ui/client", "bknd/client")); + + delayTypes(); + } +}); + /** * Building adapters */ diff --git a/app/package.json b/app/package.json index 8baeefd..e20421b 100644 --- a/app/package.json +++ b/app/package.json @@ -17,7 +17,7 @@ "build:types": "tsc --emitDeclarationOnly && tsc-alias", "updater": "bun x npm-check-updates -ui", "cli": "LOCAL=1 bun src/cli/index.ts", - "prepublishOnly": "bun run test && bun run build:all" + "prepublishOnly": "bun run types && bun run test && bun run build:all" }, "license": "FSL-1.1-MIT", "dependencies": { @@ -29,12 +29,12 @@ "dayjs": "^1.11.13", "fast-xml-parser": "^4.4.0", "hono": "^4.6.12", + "json-schema-form-react": "^0.0.2", "kysely": "^0.27.4", "liquidjs": "^10.15.0", "lodash-es": "^4.17.21", "oauth4webapi": "^2.11.1", - "swr": "^2.2.5", - "json-schema-form-react": "^0.0.2" + "swr": "^2.2.5" }, "devDependencies": { "@aws-sdk/client-s3": "^3.613.0", @@ -62,6 +62,7 @@ "@vitejs/plugin-react": "^4.3.3", "@xyflow/react": "^12.3.2", "autoprefixer": "^10.4.20", + "clsx": "^2.1.1", "esbuild-postcss": "^0.0.4", "jotai": "^2.10.1", "open": "^10.1.0", @@ -168,7 +169,8 @@ "import": "./dist/adapter/astro/index.js", "require": "./dist/adapter/astro/index.cjs" }, - "./dist/styles.css": "./dist/ui/main.css", + "./dist/main.css": "./dist/ui/main.css", + "./dist/styles.css": "./dist/ui/styles.css", "./dist/manifest.json": "./dist/static/.vite/manifest.json" }, "publishConfig": { diff --git a/app/src/media/api/MediaApi.ts b/app/src/media/api/MediaApi.ts index 121c2fc..722f94d 100644 --- a/app/src/media/api/MediaApi.ts +++ b/app/src/media/api/MediaApi.ts @@ -1,5 +1,5 @@ import { type BaseModuleApiOptions, ModuleApi, type PrimaryFieldType } from "modules/ModuleApi"; -import type { FileWithPath } from "ui/modules/media/components/dropzone/file-selector"; +import type { FileWithPath } from "ui/elements/media/file-selector"; export type MediaApiOptions = BaseModuleApiOptions & {}; diff --git a/app/src/media/index.ts b/app/src/media/index.ts index 7a1cdc7..71bcc80 100644 --- a/app/src/media/index.ts +++ b/app/src/media/index.ts @@ -1,7 +1,8 @@ import type { TObject, TString } from "@sinclair/typebox"; import { type Constructor, Registry } from "core"; -export { MIME_TYPES } from "./storage/mime-types"; +//export { MIME_TYPES } from "./storage/mime-types"; +export { guess as guessMimeType } from "./storage/mime-types-tiny"; export { Storage, type StorageAdapter, @@ -19,7 +20,7 @@ import { type S3AdapterConfig, StorageS3Adapter } from "./storage/adapters/Stora export { StorageS3Adapter, type S3AdapterConfig, StorageCloudinaryAdapter, type CloudinaryConfig }; export * as StorageEvents from "./storage/events"; -export { type FileUploadedEventData } from "./storage/events"; +export type { FileUploadedEventData } from "./storage/events"; export * from "./utils"; type ClassThatImplements = Constructor & { prototype: T }; diff --git a/app/src/ui/client/ClientProvider.tsx b/app/src/ui/client/ClientProvider.tsx index 4fd6719..3a81775 100644 --- a/app/src/ui/client/ClientProvider.tsx +++ b/app/src/ui/client/ClientProvider.tsx @@ -12,7 +12,6 @@ export type ClientProviderProps = { }; export const ClientProvider = ({ children, baseUrl, user }: ClientProviderProps) => { - //const [actualBaseUrl, setActualBaseUrl] = useState(null); const winCtx = useBkndWindowContext(); const _ctx_baseUrl = useBaseUrl(); let actualBaseUrl = baseUrl ?? _ctx_baseUrl ?? ""; @@ -31,6 +30,7 @@ export const ClientProvider = ({ children, baseUrl, user }: ClientProviderProps) console.error("error .....", e); } + console.log("api init", { host: actualBaseUrl, user: user ?? winCtx.user }); const api = new Api({ host: actualBaseUrl, user: user ?? winCtx.user }); return ( diff --git a/app/src/ui/client/schema/auth/use-auth.ts b/app/src/ui/client/schema/auth/use-auth.ts index fd2ec84..9ea39bf 100644 --- a/app/src/ui/client/schema/auth/use-auth.ts +++ b/app/src/ui/client/schema/auth/use-auth.ts @@ -73,23 +73,3 @@ export const useAuth = (options?: { baseUrl?: string }): UseAuth => { verify }; }; - -type AuthStrategyData = Pick; -export const useAuthStrategies = (options?: { baseUrl?: string }): Partial & { - loading: boolean; -} => { - const [data, setData] = useState(); - const api = useApi(options?.baseUrl); - - useEffect(() => { - (async () => { - const res = await api.auth.strategies(); - //console.log("res", res); - if (res.res.ok) { - setData(res.body); - } - })(); - }, [options?.baseUrl]); - - return { strategies: data?.strategies, basepath: data?.basepath, loading: !data }; -}; diff --git a/app/src/ui/components/form/Formy/BooleanInputMantine.tsx b/app/src/ui/components/form/Formy/BooleanInputMantine.tsx new file mode 100644 index 0000000..3f3c77a --- /dev/null +++ b/app/src/ui/components/form/Formy/BooleanInputMantine.tsx @@ -0,0 +1,29 @@ +import { Switch } from "@mantine/core"; +import { forwardRef, useEffect, useState } from "react"; + +export const BooleanInputMantine = forwardRef>( + (props, ref) => { + const [checked, setChecked] = useState(Boolean(props.value)); + + useEffect(() => { + setChecked(Boolean(props.value)); + }, [props.value]); + + function handleCheck(e) { + setChecked(e.target.checked); + props.onChange?.(e.target.checked); + } + + return ( +
+ +
+ ); + } +); diff --git a/app/src/ui/components/form/Formy.tsx b/app/src/ui/components/form/Formy/components.tsx similarity index 92% rename from app/src/ui/components/form/Formy.tsx rename to app/src/ui/components/form/Formy/components.tsx index af2eb49..d725238 100644 --- a/app/src/ui/components/form/Formy.tsx +++ b/app/src/ui/components/form/Formy/components.tsx @@ -1,11 +1,10 @@ -import { Switch } from "@mantine/core"; import { getBrowser } from "core/utils"; import type { Field } from "data"; import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react"; import { TbCalendar, TbChevronDown, TbInfoCircle } from "react-icons/tb"; import { twMerge } from "tailwind-merge"; -import { useEvent } from "../../hooks/use-event"; -import { IconButton } from "../buttons/IconButton"; +import { IconButton } from "ui/components/buttons/IconButton"; +import { useEvent } from "ui/hooks/use-event"; export const Group: React.FC & { error?: boolean }> = ({ error, @@ -131,17 +130,6 @@ export const BooleanInput = forwardRef - - - ); - /*return (
- );*/ + ); } ); diff --git a/app/src/ui/components/form/Formy/index.ts b/app/src/ui/components/form/Formy/index.ts new file mode 100644 index 0000000..04555f6 --- /dev/null +++ b/app/src/ui/components/form/Formy/index.ts @@ -0,0 +1,17 @@ +import { BooleanInputMantine } from "./BooleanInputMantine"; +import { DateInput, Input, Textarea } from "./components"; + +export const formElementFactory = (element: string, props: any) => { + switch (element) { + case "date": + return DateInput; + case "boolean": + return BooleanInputMantine; + case "textarea": + return Textarea; + default: + return Input; + } +}; + +export * from "./components"; diff --git a/app/src/ui/modules/auth/AuthForm.tsx b/app/src/ui/elements/auth/AuthForm.tsx similarity index 94% rename from app/src/ui/modules/auth/AuthForm.tsx rename to app/src/ui/elements/auth/AuthForm.tsx index fa864ef..88ce0ca 100644 --- a/app/src/ui/modules/auth/AuthForm.tsx +++ b/app/src/ui/elements/auth/AuthForm.tsx @@ -1,13 +1,13 @@ import type { ValueError } from "@sinclair/typebox/value"; import type { AppAuthOAuthStrategy, AppAuthSchema } from "auth/auth-schema"; +import clsx from "clsx"; import { type TSchema, Type, Value } from "core/utils"; import { Form, type Validator } from "json-schema-form-react"; import { transform } from "lodash-es"; import type { ComponentPropsWithoutRef } from "react"; -import { twMerge } from "tailwind-merge"; import { Button } from "ui/components/buttons/Button"; -import { Group, Input, Label } from "ui/components/form/Formy"; -import { SocialLink } from "ui/modules/auth/SocialLink"; +import { Group, Input, Label } from "ui/components/form/Formy/components"; +import { SocialLink } from "./SocialLink"; export type LoginFormProps = Omit, "onSubmit" | "action"> & { className?: string; @@ -86,7 +86,7 @@ export function AuthForm({ schema={schema} validator={validator} validationMode="change" - className={twMerge("flex flex-col gap-3 w-full", className)} + className={clsx("flex flex-col gap-3 w-full", className)} > {({ errors, submitting }) => ( <> diff --git a/app/src/ui/modules/auth/AuthScreen.tsx b/app/src/ui/elements/auth/AuthScreen.tsx similarity index 70% rename from app/src/ui/modules/auth/AuthScreen.tsx rename to app/src/ui/elements/auth/AuthScreen.tsx index 3ac60e1..340a89e 100644 --- a/app/src/ui/modules/auth/AuthScreen.tsx +++ b/app/src/ui/elements/auth/AuthScreen.tsx @@ -1,8 +1,6 @@ import type { ReactNode } from "react"; -import { useAuthStrategies } from "ui/client/schema/auth/use-auth"; -import { Logo } from "ui/components/display/Logo"; -import { Link } from "ui/components/wouter/Link"; -import { AuthForm } from "ui/modules/auth/AuthForm"; +import { useAuthStrategies } from "../hooks/use-auth"; +import { AuthForm } from "./AuthForm"; export type AuthScreenProps = { method?: "POST" | "GET"; @@ -18,13 +16,7 @@ export function AuthScreen({ method = "POST", action = "login", logo, intro }: A
{!loading && (
- {typeof logo !== "undefined" ? ( - logo - ) : ( - - - - )} + {logo ? logo : null} {typeof intro !== "undefined" ? ( intro ) : ( diff --git a/app/src/ui/modules/auth/SocialLink.tsx b/app/src/ui/elements/auth/SocialLink.tsx similarity index 100% rename from app/src/ui/modules/auth/SocialLink.tsx rename to app/src/ui/elements/auth/SocialLink.tsx diff --git a/app/src/ui/elements/auth/index.ts b/app/src/ui/elements/auth/index.ts new file mode 100644 index 0000000..b73224a --- /dev/null +++ b/app/src/ui/elements/auth/index.ts @@ -0,0 +1,9 @@ +import { AuthForm } from "./AuthForm"; +import { AuthScreen } from "./AuthScreen"; +import { SocialLink } from "./SocialLink"; + +export const Auth = { + Screen: AuthScreen, + Form: AuthForm, + SocialLink: SocialLink +}; diff --git a/app/src/ui/elements/hooks/use-auth.ts b/app/src/ui/elements/hooks/use-auth.ts new file mode 100644 index 0000000..5907cf6 --- /dev/null +++ b/app/src/ui/elements/hooks/use-auth.ts @@ -0,0 +1,23 @@ +import type { AppAuthSchema } from "auth/auth-schema"; +import { useEffect, useState } from "react"; +import { useApi } from "ui/client"; + +type AuthStrategyData = Pick; +export const useAuthStrategies = (options?: { baseUrl?: string }): Partial & { + loading: boolean; +} => { + const [data, setData] = useState(); + const api = useApi(options?.baseUrl); + + useEffect(() => { + (async () => { + const res = await api.auth.strategies(); + //console.log("res", res); + if (res.res.ok) { + setData(res.body); + } + })(); + }, [options?.baseUrl]); + + return { strategies: data?.strategies, basepath: data?.basepath, loading: !data }; +}; diff --git a/app/src/ui/elements/index.ts b/app/src/ui/elements/index.ts index 83a292b..c2d2109 100644 --- a/app/src/ui/elements/index.ts +++ b/app/src/ui/elements/index.ts @@ -1,2 +1,2 @@ -export { Auth } from "ui/modules/auth/index"; +export { Auth } from "./auth"; export * from "./media"; diff --git a/app/src/ui/elements/media.ts b/app/src/ui/elements/media.ts deleted file mode 100644 index 5ed6e11..0000000 --- a/app/src/ui/elements/media.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { PreviewWrapperMemoized } from "ui/modules/media/components/dropzone/Dropzone"; -import { DropzoneContainer } from "ui/modules/media/components/dropzone/DropzoneContainer"; - -export const Media = { - Dropzone: DropzoneContainer, - Preview: PreviewWrapperMemoized -}; - -export type { - PreviewComponentProps, - FileState, - DropzoneProps, - DropzoneRenderProps -} from "ui/modules/media/components/dropzone/Dropzone"; -export type { DropzoneContainerProps } from "ui/modules/media/components/dropzone/DropzoneContainer"; diff --git a/app/src/ui/modules/media/components/dropzone/Dropzone.tsx b/app/src/ui/elements/media/Dropzone.tsx similarity index 100% rename from app/src/ui/modules/media/components/dropzone/Dropzone.tsx rename to app/src/ui/elements/media/Dropzone.tsx diff --git a/app/src/ui/modules/media/components/dropzone/DropzoneContainer.tsx b/app/src/ui/elements/media/DropzoneContainer.tsx similarity index 92% rename from app/src/ui/modules/media/components/dropzone/DropzoneContainer.tsx rename to app/src/ui/elements/media/DropzoneContainer.tsx index 96c65c2..87c9933 100644 --- a/app/src/ui/modules/media/components/dropzone/DropzoneContainer.tsx +++ b/app/src/ui/elements/media/DropzoneContainer.tsx @@ -4,13 +4,8 @@ import type { TAppMediaConfig } from "media/media-schema"; import { useId } from "react"; import { useApi, useBaseUrl, useEntityQuery, useInvalidate } from "ui/client"; import { useEvent } from "ui/hooks/use-event"; -import { - Dropzone, - type DropzoneProps, - type DropzoneRenderProps, - type FileState -} from "ui/modules/media/components/dropzone/Dropzone"; -import { mediaItemsToFileStates } from "ui/modules/media/helper"; +import { Dropzone, type DropzoneProps, type DropzoneRenderProps, type FileState } from "./Dropzone"; +import { mediaItemsToFileStates } from "./helper"; export type DropzoneContainerProps = { children?: (props: DropzoneRenderProps) => JSX.Element; diff --git a/app/src/ui/modules/media/components/dropzone/file-selector.ts b/app/src/ui/elements/media/file-selector.ts similarity index 97% rename from app/src/ui/modules/media/components/dropzone/file-selector.ts rename to app/src/ui/elements/media/file-selector.ts index 3695dd2..d718893 100644 --- a/app/src/ui/modules/media/components/dropzone/file-selector.ts +++ b/app/src/ui/elements/media/file-selector.ts @@ -4,7 +4,7 @@ * MIT License (2020 Roland Groza) */ -import { MIME_TYPES } from "media"; +import { guess } from "media/storage/mime-types-tiny"; const FILES_TO_IGNORE = [ // Thumbnail cache files for macOS and Windows @@ -47,10 +47,8 @@ function withMimeType(file: FileWithPath) { console.log("withMimeType", name, hasExtension); if (hasExtension && !file.type) { - const ext = name.split(".").pop()!.toLowerCase(); - const type = MIME_TYPES.get(ext); - - console.log("withMimeType:in", ext, type); + const type = guess(name); + console.log("guessed", type); if (type) { Object.defineProperty(file, "type", { diff --git a/app/src/ui/modules/media/helper.ts b/app/src/ui/elements/media/helper.ts similarity index 92% rename from app/src/ui/modules/media/helper.ts rename to app/src/ui/elements/media/helper.ts index 78f6253..fa4bde2 100644 --- a/app/src/ui/modules/media/helper.ts +++ b/app/src/ui/elements/media/helper.ts @@ -1,5 +1,5 @@ import type { MediaFieldSchema } from "media/AppMedia"; -import type { FileState } from "./components/dropzone/Dropzone"; +import type { FileState } from "./Dropzone"; export function mediaItemToFileState( item: MediaFieldSchema, diff --git a/app/src/ui/elements/media/index.ts b/app/src/ui/elements/media/index.ts new file mode 100644 index 0000000..142d2a7 --- /dev/null +++ b/app/src/ui/elements/media/index.ts @@ -0,0 +1,15 @@ +import { PreviewWrapperMemoized } from "./Dropzone"; +import { DropzoneContainer } from "./DropzoneContainer"; + +export const Media = { + Dropzone: DropzoneContainer, + Preview: PreviewWrapperMemoized +}; + +export type { + PreviewComponentProps, + FileState, + DropzoneProps, + DropzoneRenderProps +} from "./Dropzone"; +export type { DropzoneContainerProps } from "./DropzoneContainer"; diff --git a/app/src/ui/modules/media/components/dropzone/use-dropzone.ts b/app/src/ui/elements/media/use-dropzone.ts similarity index 100% rename from app/src/ui/modules/media/components/dropzone/use-dropzone.ts rename to app/src/ui/elements/media/use-dropzone.ts diff --git a/app/src/ui/elements/mocks/tailwind-merge.ts b/app/src/ui/elements/mocks/tailwind-merge.ts new file mode 100644 index 0000000..8db229a --- /dev/null +++ b/app/src/ui/elements/mocks/tailwind-merge.ts @@ -0,0 +1,3 @@ +export function twMerge(...classes: string[]) { + return classes.filter(Boolean).join(" "); +} diff --git a/app/src/ui/main.css b/app/src/ui/main.css index 6541e5a..c6254b0 100644 --- a/app/src/ui/main.css +++ b/app/src/ui/main.css @@ -1,211 +1,77 @@ -@import "./components/form/json-schema/styles.css"; -@import "@xyflow/react/dist/style.css"; -@import "@mantine/core/styles.css"; -@import "@mantine/notifications/styles.css"; - @tailwind base; @tailwind components; @tailwind utilities; -html.fixed, -html.fixed body { - top: 0; - left: 0; - height: 100%; - width: 100%; - position: fixed; - overflow: hidden; - overscroll-behavior-x: contain; - touch-action: none; -} - -#bknd-admin, -.bknd-admin { - --color-primary: 9 9 11; /* zinc-950 */ - --color-background: 250 250 250; /* zinc-50 */ - --color-muted: 228 228 231; /* ? */ - --color-darkest: 0 0 0; /* black */ - --color-lightest: 255 255 255; /* white */ - - &.dark { - --color-primary: 250 250 250; /* zinc-50 */ - --color-background: 30 31 34; - --color-muted: 47 47 52; - --color-darkest: 255 255 255; /* white */ - --color-lightest: 24 24 27; /* black */ - } - - @mixin light { - --mantine-color-body: rgb(250 250 250); - } - @mixin dark { - --mantine-color-body: rgb(9 9 11); - } - - table { - font-size: inherit; - } -} - -html, -body { - font-size: 14px; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - overscroll-behavior-y: none; -} - #bknd-admin { - @apply bg-background text-primary overflow-hidden h-dvh w-dvw; + @apply bg-background text-primary overflow-hidden h-dvh w-dvw; - ::selection { - @apply bg-muted; - } + ::selection { + @apply bg-muted; + } - input { - &::selection { - @apply bg-primary/15; - } - } + input { + &::selection { + @apply bg-primary/15; + } + } } body, #bknd-admin { - @apply flex flex-1 flex-col h-dvh w-dvw; + @apply flex flex-1 flex-col h-dvh w-dvw; } @layer components { - .link { - @apply transition-colors active:translate-y-px; - } + .link { + @apply transition-colors active:translate-y-px; + } - .img-responsive { - @apply max-h-full w-auto; - } + .img-responsive { + @apply max-h-full w-auto; + } - /** - * debug classes - */ - .bordered-red { - @apply border-2 border-red-500; - } + /** + * debug classes + */ + .bordered-red { + @apply border-2 border-red-500; + } - .bordered-green { - @apply border-2 border-green-500; - } + .bordered-green { + @apply border-2 border-green-500; + } - .bordered-blue { - @apply border-2 border-blue-500; - } + .bordered-blue { + @apply border-2 border-blue-500; + } - .bordered-violet { - @apply border-2 border-violet-500; - } + .bordered-violet { + @apply border-2 border-violet-500; + } - .bordered-yellow { - @apply border-2 border-yellow-500; - } + .bordered-yellow { + @apply border-2 border-yellow-500; + } } @layer utilities { } -/* Hide scrollbar for Chrome, Safari and Opera */ -.app-scrollbar::-webkit-scrollbar { - display: none; -} -/* Hide scrollbar for IE, Edge and Firefox */ -.app-scrollbar { - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ -} - -div[data-radix-scroll-area-viewport] > div:first-child { - display: block !important; - min-width: 100% !important; - max-width: 100%; -} - -/* hide calendar icon on inputs */ -input[type="datetime-local"]::-webkit-calendar-picker-indicator, -input[type="date"]::-webkit-calendar-picker-indicator { - display: none; -} - -/* cm */ -.cm-editor { - display: flex; - flex: 1; -} - -.animate-fade-in { - animation: fadeInAnimation 200ms ease; -} -@keyframes fadeInAnimation { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -input[readonly]::placeholder, -input[disabled]::placeholder { - opacity: 0.1; -} - -.react-flow__pane, -.react-flow__renderer, -.react-flow__node, -.react-flow__edge { - cursor: inherit !important; - .drag-handle { - cursor: grab; - } -} -.react-flow .react-flow__edge path, -.react-flow__connectionline path { - stroke-width: 2; -} - -.mantine-TextInput-wrapper input { - font-family: inherit; - line-height: 1; -} - -.cm-editor { - background: transparent; -} -.cm-editor.cm-focused { - outline: none; -} - -.flex-animate { - transition: flex-grow 0.2s ease, background-color 0.2s ease; -} -.flex-initial { - flex: 0 1 auto; -} -.flex-open { - flex: 1 1 0; -} - #bknd-admin, .bknd-admin { - /* Chrome, Edge, and Safari */ - & *::-webkit-scrollbar { - @apply w-1; - &:horizontal { - @apply h-px; - } - } + /* Chrome, Edge, and Safari */ + & *::-webkit-scrollbar { + @apply w-1; + &:horizontal { + @apply h-px; + } + } - & *::-webkit-scrollbar-track { - @apply bg-transparent w-1; - } + & *::-webkit-scrollbar-track { + @apply bg-transparent w-1; + } - & *::-webkit-scrollbar-thumb { - @apply bg-primary/25; - } + & *::-webkit-scrollbar-thumb { + @apply bg-primary/25; + } } diff --git a/app/src/ui/main.tsx b/app/src/ui/main.tsx index 623e6af..74a358d 100644 --- a/app/src/ui/main.tsx +++ b/app/src/ui/main.tsx @@ -2,6 +2,7 @@ import * as React from "react"; import * as ReactDOM from "react-dom/client"; import Admin from "./Admin"; import "./main.css"; +import "./styles.css"; ReactDOM.createRoot(document.getElementById("root")!).render( diff --git a/app/src/ui/modules/auth/index.ts b/app/src/ui/modules/auth/index.ts deleted file mode 100644 index f3940d7..0000000 --- a/app/src/ui/modules/auth/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { AuthForm } from "ui/modules/auth/AuthForm"; -import { AuthScreen } from "ui/modules/auth/AuthScreen"; -import { SocialLink } from "ui/modules/auth/SocialLink"; - -export const Auth = { - Screen: AuthScreen, - Form: AuthForm, - SocialLink: SocialLink -}; diff --git a/app/src/ui/routes/auth/auth.login.tsx b/app/src/ui/routes/auth/auth.login.tsx index b9bf183..659624e 100644 --- a/app/src/ui/routes/auth/auth.login.tsx +++ b/app/src/ui/routes/auth/auth.login.tsx @@ -1,7 +1,18 @@ +import { Logo } from "ui/components/display/Logo"; +import { Link } from "ui/components/wouter/Link"; +import { Auth } from "ui/elements"; import { useBrowserTitle } from "ui/hooks/use-browser-title"; -import { AuthScreen } from "ui/modules/auth/AuthScreen"; export function AuthLogin() { useBrowserTitle(["Login"]); - return ; + return ( + + + + } + /> + ); } diff --git a/app/src/ui/styles.css b/app/src/ui/styles.css new file mode 100644 index 0000000..7c2d1d4 --- /dev/null +++ b/app/src/ui/styles.css @@ -0,0 +1,133 @@ +@import "./components/form/json-schema/styles.css"; +@import "@xyflow/react/dist/style.css"; +@import "@mantine/core/styles.css"; +@import "@mantine/notifications/styles.css"; + +html.fixed, +html.fixed body { + top: 0; + left: 0; + height: 100%; + width: 100%; + position: fixed; + overflow: hidden; + overscroll-behavior-x: contain; + touch-action: none; +} + +#bknd-admin, +.bknd-admin { + --color-primary: 9 9 11; /* zinc-950 */ + --color-background: 250 250 250; /* zinc-50 */ + --color-muted: 228 228 231; /* ? */ + --color-darkest: 0 0 0; /* black */ + --color-lightest: 255 255 255; /* white */ + + &.dark { + --color-primary: 250 250 250; /* zinc-50 */ + --color-background: 30 31 34; + --color-muted: 47 47 52; + --color-darkest: 255 255 255; /* white */ + --color-lightest: 24 24 27; /* black */ + } + + @mixin light { + --mantine-color-body: rgb(250 250 250); + } + @mixin dark { + --mantine-color-body: rgb(9 9 11); + } + + table { + font-size: inherit; + } +} + +html, +body { + font-size: 14px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + overscroll-behavior-y: none; +} + +/* Hide scrollbar for Chrome, Safari and Opera */ +.app-scrollbar::-webkit-scrollbar { + display: none; +} +/* Hide scrollbar for IE, Edge and Firefox */ +.app-scrollbar { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +div[data-radix-scroll-area-viewport] > div:first-child { + display: block !important; + min-width: 100% !important; + max-width: 100%; +} + +/* hide calendar icon on inputs */ +input[type="datetime-local"]::-webkit-calendar-picker-indicator, +input[type="date"]::-webkit-calendar-picker-indicator { + display: none; +} + +/* cm */ +.cm-editor { + display: flex; + flex: 1; +} + +.animate-fade-in { + animation: fadeInAnimation 200ms ease; +} +@keyframes fadeInAnimation { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +input[readonly]::placeholder, +input[disabled]::placeholder { + opacity: 0.1; +} + +.react-flow__pane, +.react-flow__renderer, +.react-flow__node, +.react-flow__edge { + cursor: inherit !important; + .drag-handle { + cursor: grab; + } +} +.react-flow .react-flow__edge path, +.react-flow__connectionline path { + stroke-width: 2; +} + +.mantine-TextInput-wrapper input { + font-family: inherit; + line-height: 1; +} + +.cm-editor { + background: transparent; +} +.cm-editor.cm-focused { + outline: none; +} + +.flex-animate { + transition: flex-grow 0.2s ease, background-color 0.2s ease; +} +.flex-initial { + flex: 0 1 auto; +} +.flex-open { + flex: 1 1 0; +} diff --git a/biome.json b/biome.json index 699da84..37a8584 100644 --- a/biome.json +++ b/biome.json @@ -19,6 +19,11 @@ "trailingCommas": "none" } }, + "css": { + "formatter": { + "indentWidth": 3 + } + }, "files": { "ignore": [ "**/node_modules/**", @@ -74,4 +79,4 @@ } } } -} \ No newline at end of file +} diff --git a/bun.lockb b/bun.lockb index 48020ba..09c5c1a 100755 Binary files a/bun.lockb and b/bun.lockb differ