Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7da88f8
stub: base chat interface
ElasticBottle Apr 15, 2025
099276d
feat: add basic authentication to deployed site for now
ElasticBottle Apr 15, 2025
7f4085e
feat: support image attachments
ElasticBottle Apr 16, 2025
4535db3
chore: code clean up
ElasticBottle Apr 16, 2025
fcb33fc
fix: formatting error
ElasticBottle Apr 23, 2025
76d4baf
ci: update env vars to have env variables
ElasticBottle Apr 23, 2025
4b066bc
ci: update type check to have env
ElasticBottle Apr 23, 2025
a67998b
fix: build type failures
ElasticBottle Apr 23, 2025
4584416
Merge branch 'main' of https://github.com/ElasticBottle/rectangular-l…
ElasticBottle Apr 26, 2025
12f4b89
chore: update env vars
ElasticBottle Apr 26, 2025
7f6582b
chore: clean up env reference
ElasticBottle Apr 26, 2025
e0f5a3d
chore: add personal stage deployment
ElasticBottle Apr 26, 2025
f90cb37
fix: sst config
ElasticBottle Apr 26, 2025
1335d33
fix: format errors
ElasticBottle Apr 26, 2025
34d2963
Merge branch 'winston/chat' of https://github.com/ElasticBottle/recta…
ElasticBottle Apr 26, 2025
13ab725
feat: editor package
ElasticBottle Apr 27, 2025
a953b98
feat: update list and heading components
ElasticBottle Apr 27, 2025
95f91ef
feat: clean up prosemirror buttons
ElasticBottle Apr 27, 2025
4b6cc2f
feat: add loro crdt
ElasticBottle Apr 28, 2025
251b14d
feat: update editor styling
ElasticBottle Apr 29, 2025
0486ccc
feat: add tiptap variables
ElasticBottle Apr 29, 2025
d57d620
Merge branch 'main' of https://github.com/ElasticBottle/rectangular-l…
ElasticBottle Aug 28, 2025
da0b48d
chore: package.json
ElasticBottle Aug 28, 2025
c1679c4
fix: lint and format error
ElasticBottle Aug 28, 2025
2f56968
fix: type errors
ElasticBottle Aug 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ BASIC_AUTH_PASSWORD="encrypted:BPsyMs9LTwjtb9DvNjANPL9e0Mpv+/GRdcXpQi1H3DshaUEdt
DATABASE_URL="postgresql://postgres:postgres@localhost:9876/app"
GOOGLE_GENERATIVE_AI_API_KEY="encrypted:BOpnkCvtUHsElQPq/bd0nWJfao3ZbDtU8/ryBS8iW0Bz7GiTjIqQ+4JZJNCwE2PhIa/G3NfKpAOYLhHcbK9zgQCU60JH8SurgdKMyfYJOHtNqg7MCCnE/BD2zd19hE+iYpl0q5N9S/f/wGYyey/zIxA6K5sQ0TjCN+SxQCHNXR4Pj5j05jrUlA=="

DISCORD_CLIENT_ID="encrypted:BExSni527Nj3Cjb9rOhdBvsI7DWr9rDo7/s2PTFzifHQJcWyb9LbpVjYx+9+MHlqMCVRecrlJFyuM3mkEU07xXITLLHUtzNQ59xGY0KUwqYvpj1dKnpmfDzmziE7TWEP7FUaMnKB3RK9OEkoVNNFdcQLJag="
DISCORD_CLIENT_SECRET="encrypted:BPaZhLJ6I2lehHdwhprvcWZq1HndW+DgENJf1eFAnoJhzkgV3I5TPPNSJgtIOqVB712a8URmjbFjj7VOk9CEQ4K5vu/TpxCTWuPAewkpjVqhYAW+QO2kNwEZODyyIxcI/2RWDKYbhER3cTFilZ59PGuWs6ZhNgOEWe8BToqOuzcf"
GITHUB_CLIENT_ID="encrypted:BFoVE4xZMaDxlouzJ0obBmsqmWMRWOmELLDqsn0JyZewUEdhJfC26rWqmfKvpDHgehV5o9DyWKhToGeLwmM2iShXB8rR8ejunp49xQrNt8fuhug/SSFdEmmodPOvWCKRev56sTQOEl65K5h7jj06PMt0rmvf"
GITHUB_CLIENT_SECRET="encrypted:BMRyRK+HvykXTdvIG3hP1NYzgolurm76pDxz3Z//ckbItxorOy1TPX/N11ha6KiU08P+mBrCHbjNUokJQtPIl+5cFwtnudIuPAA+Vdj6pAyIlrUt+nIA5CnkrAH9VzaG9JfBhzS56dfMkNVEnVSj+Bf8+oldSQuQ1VGfRW1yfbzDnzsK2NlGWpY="

CLOUDFLARE_API_TOKEN="encrypted:BCdyokIzh8j7Q2028lZ8ekpL0XQUEuYRDU907vzPeAb0Dfir9APnXX1f9ZszJHDtxBFX1tB2bbB97EPHGx53oHVDUuNHZ5voxPEoUd4j6TTeTYFiOQppqEpUzMgh8LdUWFdDJE48aF3D8YbC41NJTmOZ8/Yr14KcniRqCtdp6IApvYSlOk3w2f8="
CLOUDFLARE_DEFAULT_ACCOUNT_ID="encrypted:BJ6dZE7SYXyvaGfSgB8gjHkNpogMZDj5VSTXafLLsG57urj6OOh3GFde7YmWYIyMzjMaTBARTE+WzPL/8LrhxEpEx8+Y+J16SV71ZRXu2MhWDpo6Buzt+Ycn6iV4KytPHfQ221dUTHaVnqxLgYjCF3az20geiuyX2ZJjOTFSbpsT"
CLOUDFLARE_ZONE_ID="encrypted:BEuw3Ja75yhNplJ/BYWOgQdcjvVRnvJri/k8XSOdAohsZPiR/cchy1vd/1SvUXvDmqqsgBdUyEFsJ2OhRECAIILa70TvgqB8M/Yw/Lnuqr6zr2bUkGrFlYIR95/MYNbeMU+ML5fUYuAWcWq7zsqeyXhwRt6J03buSf4qLVrVJjLR"
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"editor.quickSuggestions": {
"strings": "on"
},
"typescript.enablePromptUseWorkspaceTsdk": true,
"typescript.tsdk": "node_modules/typescript/lib",
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
Expand Down Expand Up @@ -50,6 +52,7 @@
"arktype",
"Creds",
"dotenvx",
"Loro",
"millis",
"oklch",
"orpc",
Expand Down
5 changes: 5 additions & 0 deletions apps/www/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@
"with-env-prod": "dotenvx run -f ../../.env.production -- "
},
"dependencies": {
"@ai-sdk/react": "^2.0.27",
"@rectangular-labs/api": "workspace:*",
"@rectangular-labs/auth": "workspace:*",
"@rectangular-labs/editor": "workspace:*",
"@rectangular-labs/result": "workspace:*",
"@rectangular-labs/ui": "workspace:*",
"@simplewebauthn/browser": "^13.1.2",
"@t3-oss/env-core": "^0.13.8",
Expand All @@ -47,6 +50,8 @@
"typescript": "^5.9.2",
"vite": "^7.1.3",
"vite-plugin-mkcert": "^1.17.8",
"vite-plugin-top-level-await": "^1.5.0",
"vite-plugin-wasm": "^3.4.1",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.2.4",
"web-vitals": "^5.1.0"
Expand Down
24 changes: 21 additions & 3 deletions apps/www/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { createServerRootRoute } from '@tanstack/react-start/server'

import { Route as rootRouteImport } from './routes/__root'
import { Route as LoginRouteImport } from './routes/login'
import { Route as EditorRouteImport } from './routes/editor'
import { Route as AuthedRouteRouteImport } from './routes/_authed/route'
import { Route as IndexRouteImport } from './routes/index'
import { Route as AuthedOrpcRouteImport } from './routes/_authed/orpc'
Expand All @@ -25,6 +26,11 @@ const LoginRoute = LoginRouteImport.update({
path: '/login',
getParentRoute: () => rootRouteImport,
} as any)
const EditorRoute = EditorRouteImport.update({
id: '/editor',
path: '/editor',
getParentRoute: () => rootRouteImport,
} as any)
const AuthedRouteRoute = AuthedRouteRouteImport.update({
id: '/_authed',
getParentRoute: () => rootRouteImport,
Expand Down Expand Up @@ -52,32 +58,36 @@ const ApiRpcSplatServerRoute = ApiRpcSplatServerRouteImport.update({

export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/editor': typeof EditorRoute
'/login': typeof LoginRoute
'/orpc': typeof AuthedOrpcRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/editor': typeof EditorRoute
'/login': typeof LoginRoute
'/orpc': typeof AuthedOrpcRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/_authed': typeof AuthedRouteRouteWithChildren
'/editor': typeof EditorRoute
'/login': typeof LoginRoute
'/_authed/orpc': typeof AuthedOrpcRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/login' | '/orpc'
fullPaths: '/' | '/editor' | '/login' | '/orpc'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/login' | '/orpc'
id: '__root__' | '/' | '/_authed' | '/login' | '/_authed/orpc'
to: '/' | '/editor' | '/login' | '/orpc'
id: '__root__' | '/' | '/_authed' | '/editor' | '/login' | '/_authed/orpc'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AuthedRouteRoute: typeof AuthedRouteRouteWithChildren
EditorRoute: typeof EditorRoute
LoginRoute: typeof LoginRoute
}
export interface FileServerRoutesByFullPath {
Expand Down Expand Up @@ -115,6 +125,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof LoginRouteImport
parentRoute: typeof rootRouteImport
}
'/editor': {
id: '/editor'
path: '/editor'
fullPath: '/editor'
preLoaderRoute: typeof EditorRouteImport
parentRoute: typeof rootRouteImport
}
'/_authed': {
id: '/_authed'
path: ''
Expand Down Expand Up @@ -172,6 +189,7 @@ const AuthedRouteRouteWithChildren = AuthedRouteRoute._addFileChildren(
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AuthedRouteRoute: AuthedRouteRouteWithChildren,
EditorRoute: EditorRoute,
LoginRoute: LoginRoute,
}
export const routeTree = rootRouteImport
Expand Down
10 changes: 10 additions & 0 deletions apps/www/src/routes/editor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { SimpleEditor } from "@rectangular-labs/editor";
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/editor")({
component: RouteComponent,
});

function RouteComponent() {
return <SimpleEditor />;
}
196 changes: 75 additions & 121 deletions apps/www/src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,136 +1,90 @@
import * as Icons from "@rectangular-labs/ui/components/icon";
import { ChatMessageArea } from "@rectangular-labs/ui/components/chat/chat-message-area";
import { FilePreview } from "@rectangular-labs/ui/components/chat/file-preview";
import { ThemeToggle } from "@rectangular-labs/ui/components/theme-provider";
import { Button } from "@rectangular-labs/ui/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@rectangular-labs/ui/components/ui/card";
import { createFileRoute } from "@tanstack/react-router";
import { useRef, useState } from "react";

export const Route = createFileRoute("/")({
component: App,
component: ChatInterface,
});

function App() {
const data = Route.useLoaderData();
return (
<div className="min-h-screen">
<ThemeToggle className="absolute top-4 right-4" />

<div className="container mx-auto flex flex-col items-center justify-center px-4 py-16">
<h1 className="font-bold text-4xl tracking-tight">{data}</h1>
function ChatInterface() {
const [attachedFiles, setAttachedFiles] = useState<FileList | undefined>(
undefined,
);
const fileInputRef = useRef<HTMLInputElement>(null);

<p className="mt-4 text-lg">
A modern, full-stack development template
</p>
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = event.target.files;
if (!files) {
return;
}
const currentFileCount = attachedFiles?.length ?? 0;
if (currentFileCount + files.length > 5) {
alert("You can attach a maximum of 5 files.");
return;
}
setAttachedFiles((prev) => {
if (prev) {
const dt = new DataTransfer();
for (const file of prev) {
dt.items.add(file);
}
for (const file of files) {
dt.items.add(file);
}
return dt.files;
}
return files;
});
};

<div className="mt-12 grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
<Card className="flex flex-col">
<CardHeader>
<CardTitle>React + TanStack Start</CardTitle>
<CardDescription>Build modern, type-safe UIs</CardDescription>
</CardHeader>
<CardContent>
<p>
A powerful combination for building interactive web applications
with type safety and excellent developer experience.
</p>
</CardContent>
<CardFooter className="mt-auto">
<Button asChild className="w-full" variant="outline">
<a
className="flex items-center justify-center gap-2"
href="https://tanstack.com"
rel="noopener noreferrer"
target="_blank"
>
<Icons.TanStack className="h-5 w-5" />
<span>Learn TanStack</span>
</a>
</Button>
</CardFooter>
</Card>
const removeFile = (indexToRemove: number) => {
if (!attachedFiles) return;
const dt = new DataTransfer();
let index = 0;
for (const file of attachedFiles) {
if (index !== indexToRemove) {
dt.items.add(file);
}
index++;
}

<Card className="flex flex-col">
<CardHeader>
<CardTitle>Monorepo Architecture</CardTitle>
<CardDescription>Scalable project organization</CardDescription>
</CardHeader>
<CardContent>
<p>
Share code between projects, maintain consistency, and scale
your development with a modern monorepo setup.
</p>
</CardContent>
<CardFooter className="mt-auto">
<Button asChild className="w-full" variant="outline">
<a
className="flex items-center justify-center gap-2"
href="https://github.com/pnpm/pnpm"
rel="noopener noreferrer"
target="_blank"
>
<Icons.Pnpm className="h-5 w-5" />
<span>Learn More</span>
</a>
</Button>
</CardFooter>
</Card>
<Card className="flex flex-col">
<CardHeader>
<CardTitle>Typesafe APIs with oRPC</CardTitle>
<CardDescription>
End-to-end typesafe APIs made simple
</CardDescription>
</CardHeader>
<CardContent>
<p>
oRPC is a library for building end-to-end typesafe APIs. No code
generation, no schemas, just TypeScript.
</p>
</CardContent>
<CardFooter className="mt-auto">
<Button asChild className="w-full" variant="outline">
<a
className="flex items-center justify-center gap-2"
href="https://orpc.unnoq.com/"
rel="noopener noreferrer"
target="_blank"
>
<span>Learn More</span>
</a>
</Button>
</CardFooter>
</Card>
</div>
const newFileList = dt.files.length > 0 ? dt.files : undefined;
setAttachedFiles(newFileList);
};

<div className="mt-16 flex flex-col items-center space-y-6">
<Button asChild variant="default">
<a href="/orpc">
<span>Try ORPC Demo</span>
</a>
</Button>
return (
<div className="flex h-screen flex-col">
<ThemeToggle className="absolute top-4 right-4 z-10" />
<div className="flex-1 overflow-y-auto">
<ChatMessageArea>
<div className="mx-auto flex w-full max-w-3xl flex-col gap-4 pt-5 pb-5"></div>
</ChatMessageArea>
</div>

<Button asChild className="gap-2" variant="secondary">
<a
href="https://github.com/ElasticBottle/monorepo-template"
rel="noopener noreferrer"
target="_blank"
>
<Icons.GitHub className="h-5 w-5" />
<span>View on GitHub</span>
</a>
</Button>
<div className="flex w-full justify-center p-2">
<div className="w-full max-w-3xl">
<input
accept="image/*,application/pdf,.doc,.docx,.txt,.md"
className="hidden"
multiple
onChange={handleFileChange}
ref={fileInputRef}
type="file"
/>

<p className="mt-8 text-sm">
Edit{" "}
<code className="rounded px-1 py-0.5">src/routes/index.tsx</code> to
customize this page
</p>
{attachedFiles && attachedFiles.length > 0 && (
<div className="flex max-h-20 flex-wrap gap-2 overflow-y-auto pb-2">
{Array.from(attachedFiles).map((file, index) => (
<FilePreview
file={file}
key={`${file.name}-${index}`}
onRemove={() => removeFile(index)}
/>
))}
</div>
)}
</div>
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion apps/www/src/style.css
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
@import "@rectangular-labs/ui/styles.css";
@import "@rectangular-labs/ui/style.css";
@import "@rectangular-labs/editor/style.css";
@source "./**/*.{ts,tsx}";
4 changes: 4 additions & 0 deletions apps/www/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import viteReact from "@vitejs/plugin-react";
import { createJiti } from "jiti";
import mkcert from "vite-plugin-mkcert";
import topLevelAwait from "vite-plugin-top-level-await";
import wasm from "vite-plugin-wasm";
import viteTsConfigPaths from "vite-tsconfig-paths";
import { defineConfig } from "vitest/config";
import type { serverEnv } from "~/lib/env";
Expand All @@ -19,6 +21,8 @@ const config = defineConfig({
}),
tailwindcss(),
mkcert(),
wasm(),
topLevelAwait(),
tanstackStart({
customViteReactPlugin: true,
}),
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
"db:migrate-push": "bun run --filter @rectangular-labs/db migrate-push",
"db:push": "bun run --filter @rectangular-labs/db push --force",
"db:studio": "bun run --filter @rectangular-labs/db studio",
"dev": "pnpm with-env-local pnpx sst dev",
"dev": "docker compose up -d && pnpm with-env-local turbo run dev",
"dev:packages": "docker compose up -d && turbo run dev --filter=\"./packages/*\"",
"deploy:local": "pnpm with-env-local sst deploy",
"deploy:personal": "pnpm with-env-preview sst deploy",
"deploy:preview": "pnpm with-env-preview sst deploy --stage preview",
"deploy:prod": "pnpm with-env-prod sst deploy --stage production",
"env:get": "bun x dotenvx get",
"env:set": "bun x dotenvx set",
"env:view": "bun x dotenvx decrypt --stdout",
"format": "turbo run format --continue",
"lint": "turbo run lint --continue",
"new:package": "turbo gen package",
Expand Down
Loading
Loading