Compare commits
No commits in common. "5a49648e64fe032e91ea5d20d44cf36df1c6048b" and "a4bf466ba97c685597381c08d8da2251a158ff28" have entirely different histories.
5a49648e64
...
a4bf466ba9
23 changed files with 108 additions and 85 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"editor.codeActionsOnSave": {
|
||||
"quickfix.biome": "explicit",
|
||||
"source.organizeImports.biome": "explicit"
|
||||
"quickfix.biome": "explicit"
|
||||
},
|
||||
"typescript.inlayHints.parameterNames.enabled": "all",
|
||||
"[typescript]": {
|
||||
|
|
8
bun.lock
8
bun.lock
|
@ -4,11 +4,11 @@
|
|||
"": {
|
||||
"name": "signalstats",
|
||||
"dependencies": {
|
||||
"@duskflower/signal-decrypt-backup-wasm": "^0.3.0",
|
||||
"@duskflower/signal-decrypt-backup-wasm": "^0.2.1",
|
||||
"@kobalte/core": "^0.13.7",
|
||||
"@kobalte/tailwindcss": "^0.9.0",
|
||||
"@solid-primitives/refs": "^1.1.0",
|
||||
"@solid-primitives/storage": "^4.3.1",
|
||||
"@solid-primitives/storage": "^4.3.0",
|
||||
"@solid-primitives/upload": "^0.1.0",
|
||||
"@solid-primitives/workers": "^0.4.0",
|
||||
"@solidjs/meta": "^0.29.4",
|
||||
|
@ -37,7 +37,7 @@
|
|||
"@commitlint/cli": "^19.6.1",
|
||||
"@commitlint/config-conventional": "^19.6.0",
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"@types/node": "^22.10.10",
|
||||
"@types/node": "^22.10.9",
|
||||
"better-sqlite3": "^11.8.1",
|
||||
"husky": "^9.1.7",
|
||||
"kysely-codegen": "^0.17.0",
|
||||
|
@ -141,7 +141,7 @@
|
|||
|
||||
"@corvu/utils": ["@corvu/utils@0.4.2", "", { "dependencies": { "@floating-ui/dom": "^1.6.11" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-Ox2kYyxy7NoXdKWdHeDEjZxClwzO4SKM8plAaVwmAJPxHMqA0rLOoAsa+hBDwRLpctf+ZRnAd/ykguuJidnaTA=="],
|
||||
|
||||
"@duskflower/signal-decrypt-backup-wasm": ["@duskflower/signal-decrypt-backup-wasm@0.3.0", "https://git.duskflower.dev/api/packages/duskflower/npm/%40duskflower%2Fsignal-decrypt-backup-wasm/-/0.3.0/signal-decrypt-backup-wasm-0.3.0.tgz", {}, "sha512-1IMNFGz4bV4o29eXVelNbancqovsljmM0iZRrtau81gSy8bFE1lgwGFN05NAOFWlX/aZYzeUEeZVBrjUYIM0/w=="],
|
||||
"@duskflower/signal-decrypt-backup-wasm": ["@duskflower/signal-decrypt-backup-wasm@0.2.1", "https://git.duskflower.dev/api/packages/duskflower/npm/%40duskflower%2Fsignal-decrypt-backup-wasm/-/0.2.1/signal-decrypt-backup-wasm-0.2.1.tgz", {}, "sha512-lYwtubVOUU4kuJNw28Ns8N9Gh2nZB4j6BJz/vLpxF0Vc7zww3VIGnMCQToFWWSAwCs5+cABadeZMkv87SGfyQg=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.24.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA=="],
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
"@commitlint/cli": "^19.6.1",
|
||||
"@commitlint/config-conventional": "^19.6.0",
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"@types/node": "^22.10.10",
|
||||
"@types/node": "^22.10.9",
|
||||
"better-sqlite3": "^11.8.1",
|
||||
"husky": "^9.1.7",
|
||||
"kysely-codegen": "^0.17.0",
|
||||
|
@ -28,11 +28,11 @@
|
|||
"vite-plugin-wasm": "^3.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@duskflower/signal-decrypt-backup-wasm": "^0.3.0",
|
||||
"@duskflower/signal-decrypt-backup-wasm": "^0.2.1",
|
||||
"@kobalte/core": "^0.13.7",
|
||||
"@kobalte/tailwindcss": "^0.9.0",
|
||||
"@solid-primitives/refs": "^1.1.0",
|
||||
"@solid-primitives/storage": "^4.3.1",
|
||||
"@solid-primitives/storage": "^4.3.0",
|
||||
"@solid-primitives/upload": "^0.1.0",
|
||||
"@solid-primitives/workers": "^0.4.0",
|
||||
"@solidjs/meta": "^0.29.4",
|
||||
|
|
11
src/App.tsx
11
src/App.tsx
|
@ -1,15 +1,16 @@
|
|||
import { A, Route, Router, useNavigate } from "@solidjs/router";
|
||||
import { type Component, Show, Suspense, createEffect } from "solid-js";
|
||||
import { DmId, GroupId, Home, Overview, Privacy, preloadDmId } from "./pages";
|
||||
import { createEffect, Show, Suspense, type Component } from "solid-js";
|
||||
import { DmId, GroupId, Home, Overview, preloadDmId, Privacy } from "./pages";
|
||||
|
||||
import "./app.css";
|
||||
import { ColorModeProvider, ColorModeScript, createLocalStorageManager } from "@kobalte/core";
|
||||
import { MetaProvider } from "@solidjs/meta";
|
||||
import { Portal } from "solid-js/web";
|
||||
import { Callout, CalloutContent, CalloutTitle } from "./components/ui/callout";
|
||||
import { ModeToggle } from "./components/ui/mode-toggle";
|
||||
import { Callout, CalloutTitle, CalloutContent } from "./components/ui/callout";
|
||||
import { dbLoaded } from "./db";
|
||||
import { hasCashedData } from "./lib/db-cache";
|
||||
import { isWasmSupported } from "./lib/utils";
|
||||
import { ColorModeProvider, ColorModeScript, createLocalStorageManager } from "@kobalte/core";
|
||||
import { ModeToggle } from "./components/ui/mode-toggle";
|
||||
|
||||
const NO_DATA_NEEDED_PAGES = ["/", "/privacy"];
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { type NotNull, sql } from "kysely";
|
||||
import type { MainToWorkerMsg, WorkerToMainMsg } from "~/lib/kysely-official-wasm-worker/type";
|
||||
import { sql, type NotNull } from "kysely";
|
||||
import { worker, kyselyDb, SELF_ID, DB_FILENAME, setDbLoaded } from "./db";
|
||||
import { cached, clearDbCache } from "../lib/db-cache";
|
||||
import { DB_FILENAME, SELF_ID, kyselyDb, setDbLoaded, worker } from "./db";
|
||||
import type { MainToWorkerMsg, WorkerToMainMsg } from "~/lib/kysely-official-wasm-worker/type";
|
||||
|
||||
export const loadDb = async (statements: string[], progressCallback?: (percentage: number) => void): Promise<void> => {
|
||||
// try to persist storage, https://web.dev/articles/persistent-storage#request_persistent_storage
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Kysely } from "kysely";
|
||||
import { createSignal } from "solid-js";
|
||||
import { OfficialWasmWorkerDialect } from "~/lib/kysely-official-wasm-worker";
|
||||
import type { DB } from "./db-schema";
|
||||
import { OfficialWasmWorkerDialect } from "~/lib/kysely-official-wasm-worker";
|
||||
import WasmWorker from "./db-worker?worker";
|
||||
import { createSignal } from "solid-js";
|
||||
|
||||
export const SELF_ID = 2;
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { BackupDecryptor } from "@duskflower/signal-decrypt-backup-wasm";
|
||||
import {
|
||||
BackupDecryptor,
|
||||
type DecryptionResult,
|
||||
} from "@duskflower/signal-decrypt-backup-wasm";
|
||||
|
||||
const CHUNK_SIZE = 1024 * 1024 * 40; // 40MB chunks
|
||||
|
||||
|
@ -6,8 +9,7 @@ export async function decryptBackup(
|
|||
file: File,
|
||||
passphrase: string,
|
||||
progressCallback: (progress: number) => void,
|
||||
statementsCallback?: (statements: string[]) => void | Promise<void>,
|
||||
): Promise<string[]> {
|
||||
): Promise<DecryptionResult> {
|
||||
const fileSize = file.size;
|
||||
const decryptor = new BackupDecryptor();
|
||||
decryptor.set_progress_callback(fileSize, progressCallback);
|
||||
|
@ -32,8 +34,6 @@ export async function decryptBackup(
|
|||
}
|
||||
}
|
||||
|
||||
await statementsCallback?.(decryptor.get_new_decrypted_statements());
|
||||
|
||||
offset += CHUNK_SIZE;
|
||||
}
|
||||
|
|
@ -1,4 +1,11 @@
|
|||
import type { DatabaseIntrospector, Dialect, DialectAdapter, Driver, Kysely, QueryCompiler } from "kysely";
|
||||
import type {
|
||||
DatabaseIntrospector,
|
||||
Dialect,
|
||||
DialectAdapter,
|
||||
Driver,
|
||||
Kysely,
|
||||
QueryCompiler,
|
||||
} from "kysely";
|
||||
import { SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler } from "kysely";
|
||||
import { OfficialWasmWorkerDriver } from "./driver";
|
||||
import type { OfficialWasmWorkerDialectConfig } from "./type";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { MessageOverview, MessageStats, Recipients } from "~/types";
|
||||
import { getHourList, getMonthList, getWeekdayList } from "./date";
|
||||
import type { MessageOverview, MessageStats, Recipients } from "~/types";
|
||||
|
||||
const hourNames = getHourList();
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { MessageOverview, MessageStats, Recipients } from "~/types";
|
||||
import { getHourList, getMonthList, getWeekdayList } from "./date";
|
||||
import { cached } from "./db-cache";
|
||||
import { getHourList, getMonthList, getWeekdayList } from "./date";
|
||||
import MessageStatsWorker from "./messages-worker?worker";
|
||||
|
||||
export const hourNames = getHourList();
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
import { Title } from "@solidjs/meta";
|
||||
import { type RoutePreloadFunc, type RouteSectionProps, createAsync } from "@solidjs/router";
|
||||
import { type Component, Suspense } from "solid-js";
|
||||
import { Flex } from "~/components/ui/flex";
|
||||
import { Grid } from "~/components/ui/grid";
|
||||
import { Heading } from "~/components/ui/heading";
|
||||
import { SELF_ID, dmPartnerRecipientQuery, threadMostUsedWordsQuery, threadSentMessagesOverviewQuery } from "~/db";
|
||||
import { Suspense, type Component } from "solid-js";
|
||||
import { createAsync, type RoutePreloadFunc, type RouteSectionProps } from "@solidjs/router";
|
||||
|
||||
import { dmPartnerRecipientQuery, threadMostUsedWordsQuery, threadSentMessagesOverviewQuery, SELF_ID } from "~/db";
|
||||
import { getNameFromRecipient } from "~/lib/get-name-from-recipient";
|
||||
import { createMessageStatsSources } from "~/lib/messages";
|
||||
import type { MessageOverview } from "~/types";
|
||||
import { Heading } from "~/components/ui/heading";
|
||||
import { Grid } from "~/components/ui/grid";
|
||||
import { Title } from "@solidjs/meta";
|
||||
import { DmMessagesPerDate } from "./dm-messages-per-date";
|
||||
import { DmMessagesPerDaytime } from "./dm-messages-per-daytime";
|
||||
import { DmMessagesPerMonth } from "./dm-messages-per-month";
|
||||
import { DmMessagesPerRecipient } from "./dm-messages-per-recipients";
|
||||
import { DmMessagesPerWeekday } from "./dm-messages-per-weekday";
|
||||
import { DmOverview } from "./dm-overview";
|
||||
import { DmWordCloud } from "./dm-wordcloud";
|
||||
import { DmMessagesPerMonth } from "./dm-messages-per-month";
|
||||
import { DmMessagesPerDaytime } from "./dm-messages-per-daytime";
|
||||
import { DmMessagesPerRecipient } from "./dm-messages-per-recipients";
|
||||
import { DmMessagesPerWeekday } from "./dm-messages-per-weekday";
|
||||
import type { MessageOverview } from "~/types";
|
||||
import { createMessageStatsSources } from "~/lib/messages";
|
||||
import { Flex } from "~/components/ui/flex";
|
||||
|
||||
const getDmIdData = (dmId: number) => {
|
||||
// the other person in the chat with name and id
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Show, type Accessor, type Component } from "solid-js";
|
||||
import type { ChartData } from "chart.js";
|
||||
import { type Accessor, type Component, Show } from "solid-js";
|
||||
import { LineChart } from "~/components/ui/charts";
|
||||
import type { MessageStats, Recipients } from "~/types";
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Show, type Accessor, type Component } from "solid-js";
|
||||
import type { ChartData } from "chart.js";
|
||||
import { type Accessor, type Component, Show } from "solid-js";
|
||||
import { BarChart } from "~/components/ui/charts";
|
||||
import { hourNames } from "~/lib/messages";
|
||||
import type { MessageStats, Recipients } from "~/types";
|
||||
import { hourNames } from "~/lib/messages";
|
||||
|
||||
export const DmMessagesPerDaytime: Component<{
|
||||
daytimeStats: MessageStats["daytime"] | undefined;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Show, type Accessor, type Component } from "solid-js";
|
||||
import type { ChartData } from "chart.js";
|
||||
import { type Accessor, type Component, Show } from "solid-js";
|
||||
import { RadarChart } from "~/components/ui/charts";
|
||||
import { monthNames } from "~/lib/messages";
|
||||
import type { MessageStats, Recipients } from "~/types";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Show, type Accessor, type Component } from "solid-js";
|
||||
import type { ChartData } from "chart.js";
|
||||
import { type Accessor, type Component, Show } from "solid-js";
|
||||
import { PieChart } from "~/components/ui/charts";
|
||||
import type { MessageStats, Recipients } from "~/types";
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Show, type Accessor, type Component } from "solid-js";
|
||||
import type { ChartData } from "chart.js";
|
||||
import { type Accessor, type Component, Show } from "solid-js";
|
||||
import { RadarChart } from "~/components/ui/charts";
|
||||
import { weekdayNames } from "~/lib/messages";
|
||||
import type { MessageStats, Recipients } from "~/types";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { CalendarArrowDown, CalendarArrowUp, CalendarClock, MessagesSquare } from "lucide-solid";
|
||||
import { type Component, Show } from "solid-js";
|
||||
import { Show, type Component } from "solid-js";
|
||||
import { Flex } from "~/components/ui/flex";
|
||||
import { Grid } from "~/components/ui/grid";
|
||||
import { CalendarArrowDown, CalendarArrowUp, CalendarClock, MessagesSquare } from "lucide-solid";
|
||||
import { getDistanceBetweenDatesInDays } from "~/lib/date";
|
||||
import type { MessageOverview } from "~/types";
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { ChartData } from "chart.js";
|
||||
import { type Accessor, type Component, Show } from "solid-js";
|
||||
import { Show, type Accessor, type Component } from "solid-js";
|
||||
import { WordCloudChart } from "~/components/ui/charts";
|
||||
import type { threadMostUsedWordsQuery } from "~/db";
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { RouteSectionProps } from "@solidjs/router";
|
||||
import type { Component } from "solid-js";
|
||||
import type { RouteSectionProps } from "@solidjs/router";
|
||||
|
||||
export const GroupId: Component<RouteSectionProps> = (props) => {
|
||||
const groupId = () => Number(props.params.groupid);
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import { createDropzone, createFileUploader } from "@solid-primitives/upload";
|
||||
import { useNavigate, type RouteSectionProps } from "@solidjs/router";
|
||||
import { createSignal, type JSX, Show, type Component } from "solid-js";
|
||||
|
||||
import { Title } from "@solidjs/meta";
|
||||
import { type RouteSectionProps, useNavigate } from "@solidjs/router";
|
||||
import { type Component, type JSX, Show, createSignal } from "solid-js";
|
||||
import { Portal } from "solid-js/web";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { Flex } from "~/components/ui/flex";
|
||||
|
||||
import { Progress, ProgressLabel, ProgressValueLabel } from "~/components/ui/progress";
|
||||
import { TextField, TextFieldInput, TextFieldLabel } from "~/components/ui/text-field";
|
||||
import { loadDb } from "~/db";
|
||||
import { decryptBackup } from "~/lib/backup-decryptor";
|
||||
import { decryptBackup } from "~/lib/decryptor";
|
||||
import { createDropzone, createFileUploader } from "@solid-primitives/upload";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { TextField, TextFieldInput, TextFieldLabel } from "~/components/ui/text-field";
|
||||
|
||||
export const Home: Component<RouteSectionProps> = () => {
|
||||
const navigate = useNavigate();
|
||||
|
@ -32,8 +34,8 @@ export const Home: Component<RouteSectionProps> = () => {
|
|||
const [backupFile, setBackupFile] = createSignal<File>();
|
||||
|
||||
const [decryptionProgress, setDecryptionProgress] = createSignal<number>();
|
||||
const [totalStatements, setTotalStatements] = createSignal(0);
|
||||
const [executedStatements, setExecutedStatements] = createSignal(0);
|
||||
const [loadingProgress, setLoadingProgress] = createSignal<number>();
|
||||
// const [isLoadingDatabase, setIsLoadingDatabase] = createSignal(false);
|
||||
|
||||
const onSubmit: JSX.EventHandler<HTMLFormElement, SubmitEvent> = (event) => {
|
||||
event.preventDefault();
|
||||
|
@ -42,22 +44,28 @@ export const Home: Component<RouteSectionProps> = () => {
|
|||
const currentPassphrase = passphrase();
|
||||
|
||||
if (currentBackupFile && currentPassphrase) {
|
||||
console.time();
|
||||
decryptBackup(currentBackupFile, currentPassphrase, setDecryptionProgress, async (statements) => {
|
||||
const length = statements.length;
|
||||
setTotalStatements((oldValue) => oldValue + length);
|
||||
// const hashChunk = await currentBackupFile.slice(-1000).text();
|
||||
// const hash = hashString(hashChunk);
|
||||
|
||||
await loadDb(statements, (progress) => {
|
||||
setExecutedStatements((oldValue) => Math.round(oldValue + (progress / 100) * length));
|
||||
});
|
||||
// if (hash === dbHash()) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
setExecutedStatements((oldValue) => oldValue + length);
|
||||
})
|
||||
.then(() => {
|
||||
// setDbHash(hash);
|
||||
|
||||
decryptBackup(currentBackupFile, currentPassphrase, setDecryptionProgress)
|
||||
.then(async (decrypted) => {
|
||||
umami.track("Decrypt backup");
|
||||
setDecryptionProgress(undefined);
|
||||
// setIsLoadingDatabase(true);
|
||||
setLoadingProgress(0);
|
||||
|
||||
await loadDb(decrypted.database_statements, setLoadingProgress);
|
||||
umami.track("Load database");
|
||||
|
||||
console.timeEnd();
|
||||
// setIsLoadingDatabase(false);
|
||||
setLoadingProgress(undefined);
|
||||
|
||||
navigate("/overview");
|
||||
})
|
||||
.catch((error) => {
|
||||
|
@ -76,7 +84,7 @@ export const Home: Component<RouteSectionProps> = () => {
|
|||
class="fixed inset-0 gap-y-8 backdrop-blur-lg backdrop-filter"
|
||||
classList={{
|
||||
// hidden: decryptionProgress() === undefined && !isLoadingDatabase(),
|
||||
hidden: decryptionProgress() === undefined && totalStatements() === 0,
|
||||
hidden: decryptionProgress() === undefined && loadingProgress() === undefined,
|
||||
}}
|
||||
>
|
||||
<Show when={decryptionProgress() !== undefined}>
|
||||
|
@ -89,18 +97,20 @@ export const Home: Component<RouteSectionProps> = () => {
|
|||
class="w-[300px] space-y-1"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<ProgressLabel>Decrypting...</ProgressLabel>
|
||||
<ProgressLabel>Processing...</ProgressLabel>
|
||||
<ProgressValueLabel />
|
||||
</div>
|
||||
</Progress>
|
||||
</Show>
|
||||
<Show when={totalStatements() !== 0}>
|
||||
<Show when={loadingProgress() !== undefined}>
|
||||
{/* <p class="font-bold text-2xl">Loading database</p>
|
||||
<p class="text-muted-foreground">This can take some time</p> */}
|
||||
<p class="font-bold text-2xl">Loading database</p>
|
||||
<Progress
|
||||
value={executedStatements()}
|
||||
value={loadingProgress()}
|
||||
minValue={0}
|
||||
maxValue={totalStatements()}
|
||||
getValueLabel={({ value, max }) => `${value} of ${max}`}
|
||||
maxValue={100}
|
||||
getValueLabel={({ value }) => `${value}%`}
|
||||
class="w-[300px] space-y-1"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { Title } from "@solidjs/meta";
|
||||
import type { RouteSectionProps } from "@solidjs/router";
|
||||
import { type Component, Show, createResource } from "solid-js";
|
||||
import { SELF_ID, allThreadsOverviewQuery, overallSentMessagesQuery } from "~/db";
|
||||
import { type Component, createResource, Show } from "solid-js";
|
||||
|
||||
import { allThreadsOverviewQuery, overallSentMessagesQuery, SELF_ID } from "~/db";
|
||||
|
||||
import { Title } from "@solidjs/meta";
|
||||
import { getNameFromRecipient } from "~/lib/get-name-from-recipient";
|
||||
import { OverviewTable, type RoomOverview } from "./overview-table";
|
||||
|
||||
|
|
|
@ -1,29 +1,32 @@
|
|||
import { type Component, createSignal, For, Match, Show, Switch } from "solid-js";
|
||||
import { useNavigate, usePreloadRoute } from "@solidjs/router";
|
||||
|
||||
import {
|
||||
type ColumnFiltersState,
|
||||
type FilterFn,
|
||||
type SortDirection,
|
||||
type SortingState,
|
||||
createColumnHelper,
|
||||
createSolidTable,
|
||||
type FilterFn,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
type SortDirection,
|
||||
type SortingState,
|
||||
} from "@tanstack/solid-table";
|
||||
import { intlFormatDistance } from "date-fns";
|
||||
import { ArrowDown, ArrowUp, ArrowUpDown } from "lucide-solid";
|
||||
import { type Component, For, Match, Show, Switch, createSignal } from "solid-js";
|
||||
|
||||
import { Badge } from "~/components/ui/badge";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { Checkbox } from "~/components/ui/checkbox";
|
||||
import { Flex } from "~/components/ui/flex";
|
||||
import { Label } from "~/components/ui/label";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/components/ui/table";
|
||||
import { TextField, TextFieldInput } from "~/components/ui/text-field";
|
||||
import { dbLoaded, threadSentMessagesOverviewQuery } from "~/db";
|
||||
|
||||
import { cn } from "~/lib/utils";
|
||||
import { Flex } from "~/components/ui/flex";
|
||||
import { dbLoaded, threadSentMessagesOverviewQuery } from "~/db";
|
||||
|
||||
export interface RoomOverview {
|
||||
threadId: number;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import path from "path";
|
||||
import tailwind from "@tailwindcss/vite";
|
||||
import { defineConfig } from "vite";
|
||||
import solidPlugin from "vite-plugin-solid";
|
||||
import wasm from "vite-plugin-wasm";
|
||||
import tailwind from "@tailwindcss/vite";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [solidPlugin(), wasm(), tailwind()],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue