@jonsoc/web 1.1.49 → 1.1.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jonsoc/web",
3
- "version": "1.1.49",
3
+ "version": "1.1.51",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -17,8 +17,8 @@
17
17
  "@base-ui/react": "1.1.0",
18
18
  "@convex-dev/better-auth": "0.10.10",
19
19
  "@hookform/resolvers": "5.2.2",
20
- "@jonsoc/convex": "1.1.49",
21
- "@jonsoc/env": "1.1.49",
20
+ "@jonsoc/convex": "1.1.51",
21
+ "@jonsoc/env": "1.1.51",
22
22
  "@tailwindcss/vite": "4.1.11",
23
23
  "@tanstack/react-form": "1.28.0",
24
24
  "@tanstack/react-router": "1.157.18",
@@ -36,7 +36,7 @@
36
36
  "zod": "4.1.8"
37
37
  },
38
38
  "devDependencies": {
39
- "@jonsoc/config": "1.1.49",
39
+ "@jonsoc/config": "1.1.51",
40
40
  "@tanstack/react-router-devtools": "1.157.18",
41
41
  "@tanstack/router-plugin": "1.157.18",
42
42
  "@types/react": "19.2.7",
@@ -9,9 +9,16 @@
9
9
  // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10
10
 
11
11
  import { Route as rootRouteImport } from './routes/__root'
12
+ import { Route as LandingRouteImport } from './routes/landing'
12
13
  import { Route as DashboardRouteImport } from './routes/dashboard'
13
14
  import { Route as IndexRouteImport } from './routes/index'
15
+ import { Route as ShareIdRouteImport } from './routes/share.$id'
14
16
 
17
+ const LandingRoute = LandingRouteImport.update({
18
+ id: '/landing',
19
+ path: '/landing',
20
+ getParentRoute: () => rootRouteImport,
21
+ } as any)
15
22
  const DashboardRoute = DashboardRouteImport.update({
16
23
  id: '/dashboard',
17
24
  path: '/dashboard',
@@ -22,35 +29,55 @@ const IndexRoute = IndexRouteImport.update({
22
29
  path: '/',
23
30
  getParentRoute: () => rootRouteImport,
24
31
  } as any)
32
+ const ShareIdRoute = ShareIdRouteImport.update({
33
+ id: '/share/$id',
34
+ path: '/share/$id',
35
+ getParentRoute: () => rootRouteImport,
36
+ } as any)
25
37
 
26
38
  export interface FileRoutesByFullPath {
27
39
  '/': typeof IndexRoute
28
40
  '/dashboard': typeof DashboardRoute
41
+ '/landing': typeof LandingRoute
42
+ '/share/$id': typeof ShareIdRoute
29
43
  }
30
44
  export interface FileRoutesByTo {
31
45
  '/': typeof IndexRoute
32
46
  '/dashboard': typeof DashboardRoute
47
+ '/landing': typeof LandingRoute
48
+ '/share/$id': typeof ShareIdRoute
33
49
  }
34
50
  export interface FileRoutesById {
35
51
  __root__: typeof rootRouteImport
36
52
  '/': typeof IndexRoute
37
53
  '/dashboard': typeof DashboardRoute
54
+ '/landing': typeof LandingRoute
55
+ '/share/$id': typeof ShareIdRoute
38
56
  }
39
57
  export interface FileRouteTypes {
40
58
  fileRoutesByFullPath: FileRoutesByFullPath
41
- fullPaths: '/' | '/dashboard'
59
+ fullPaths: '/' | '/dashboard' | '/landing' | '/share/$id'
42
60
  fileRoutesByTo: FileRoutesByTo
43
- to: '/' | '/dashboard'
44
- id: '__root__' | '/' | '/dashboard'
61
+ to: '/' | '/dashboard' | '/landing' | '/share/$id'
62
+ id: '__root__' | '/' | '/dashboard' | '/landing' | '/share/$id'
45
63
  fileRoutesById: FileRoutesById
46
64
  }
47
65
  export interface RootRouteChildren {
48
66
  IndexRoute: typeof IndexRoute
49
67
  DashboardRoute: typeof DashboardRoute
68
+ LandingRoute: typeof LandingRoute
69
+ ShareIdRoute: typeof ShareIdRoute
50
70
  }
51
71
 
52
72
  declare module '@tanstack/react-router' {
53
73
  interface FileRoutesByPath {
74
+ '/landing': {
75
+ id: '/landing'
76
+ path: '/landing'
77
+ fullPath: '/landing'
78
+ preLoaderRoute: typeof LandingRouteImport
79
+ parentRoute: typeof rootRouteImport
80
+ }
54
81
  '/dashboard': {
55
82
  id: '/dashboard'
56
83
  path: '/dashboard'
@@ -65,12 +92,21 @@ declare module '@tanstack/react-router' {
65
92
  preLoaderRoute: typeof IndexRouteImport
66
93
  parentRoute: typeof rootRouteImport
67
94
  }
95
+ '/share/$id': {
96
+ id: '/share/$id'
97
+ path: '/share/$id'
98
+ fullPath: '/share/$id'
99
+ preLoaderRoute: typeof ShareIdRouteImport
100
+ parentRoute: typeof rootRouteImport
101
+ }
68
102
  }
69
103
  }
70
104
 
71
105
  const rootRouteChildren: RootRouteChildren = {
72
106
  IndexRoute: IndexRoute,
73
107
  DashboardRoute: DashboardRoute,
108
+ LandingRoute: LandingRoute,
109
+ ShareIdRoute: ShareIdRoute,
74
110
  }
75
111
  export const routeTree = rootRouteImport
76
112
  ._addFileChildren(rootRouteChildren)
@@ -37,10 +37,12 @@ function RootComponent() {
37
37
  <ThemeProvider attribute="class" defaultTheme="dark" disableTransitionOnChange storageKey="vite-ui-theme">
38
38
  <div className="grid grid-rows-[auto_1fr] h-svh">
39
39
  <Header />
40
+ {/* @ts-ignore - React 19 type incompatibility */}
40
41
  <Outlet />
41
42
  </div>
42
43
  <Toaster richColors />
43
44
  </ThemeProvider>
45
+ {/* @ts-ignore - React 19 type incompatibility */}
44
46
  <TanStackRouterDevtools position="bottom-left" />
45
47
  </>
46
48
  )
@@ -0,0 +1,239 @@
1
+ import { createFileRoute } from "@tanstack/react-router"
2
+ import { Button } from "@/components/ui/button"
3
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
4
+ import { useState } from "react"
5
+
6
+ export const Route = createFileRoute("/landing")({
7
+ component: LandingPage,
8
+ })
9
+
10
+ const GITHUB_REPO = "https://github.com/Noisemaker111/Jonsoc"
11
+
12
+ function LandingPage() {
13
+ const [copied, setCopied] = useState(false)
14
+
15
+ const copyInstallCommand = () => {
16
+ navigator.clipboard.writeText("bun add -g jonsoc")
17
+ setCopied(true)
18
+ setTimeout(() => setCopied(false), 2000)
19
+ }
20
+
21
+ return (
22
+ <div className="min-h-screen bg-gradient-to-b from-background to-muted">
23
+ {/* Hero Section */}
24
+ <div className="container mx-auto max-w-6xl px-4 pt-20 pb-16">
25
+ <div className="text-center">
26
+ <h1 className="mb-6 text-5xl font-bold tracking-tight sm:text-6xl lg:text-7xl">jonsoc</h1>
27
+ <p className="mx-auto mb-8 max-w-2xl text-xl text-muted-foreground">
28
+ AI-powered development tool — an open source fork of OpenCode (opencode.ai)
29
+ </p>
30
+
31
+ {/* Install Command */}
32
+ <div className="mx-auto mb-8 max-w-md">
33
+ <div className="rounded-lg border bg-card p-4 font-mono text-sm shadow-sm">
34
+ <div className="flex items-center justify-between">
35
+ <code className="text-primary">bun add -g jonsoc</code>
36
+ <Button variant="ghost" size="sm" onClick={copyInstallCommand} className="h-8">
37
+ {copied ? "Copied!" : "Copy"}
38
+ </Button>
39
+ </div>
40
+ </div>
41
+ <p className="mt-2 text-xs text-muted-foreground">
42
+ Or use npm: <code className="rounded bg-muted px-1 py-0.5">npm install -g jonsoc</code>
43
+ </p>
44
+ </div>
45
+
46
+ {/* CTA Buttons */}
47
+ <div className="flex flex-wrap items-center justify-center gap-4">
48
+ <a href={GITHUB_REPO} target="_blank" rel="noopener noreferrer">
49
+ <Button size="lg" className="gap-2">
50
+ <svg
51
+ xmlns="http://www.w3.org/2000/svg"
52
+ width="20"
53
+ height="20"
54
+ viewBox="0 0 24 24"
55
+ fill="none"
56
+ stroke="currentColor"
57
+ strokeWidth="2"
58
+ strokeLinecap="round"
59
+ strokeLinejoin="round"
60
+ >
61
+ <path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4" />
62
+ <path d="M9 18c-4.51 2-5-2-7-2" />
63
+ </svg>
64
+ View on GitHub
65
+ </Button>
66
+ </a>
67
+ <a href="/dashboard">
68
+ <Button variant="outline" size="lg" className="gap-2">
69
+ Try Web App
70
+ </Button>
71
+ </a>
72
+ </div>
73
+ </div>
74
+ </div>
75
+
76
+ {/* Features Section */}
77
+ <div className="container mx-auto max-w-6xl px-4 py-16">
78
+ <h2 className="mb-12 text-center text-3xl font-bold">Features</h2>
79
+ <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
80
+ <Card>
81
+ <CardHeader>
82
+ <CardTitle className="flex items-center gap-2 text-lg">
83
+ <svg className="h-5 w-5 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
84
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
85
+ </svg>
86
+ AI-Powered Development
87
+ </CardTitle>
88
+ </CardHeader>
89
+ <CardContent className="text-muted-foreground">
90
+ Intelligent code assistance with multiple AI providers and models.
91
+ </CardContent>
92
+ </Card>
93
+
94
+ <Card>
95
+ <CardHeader>
96
+ <CardTitle className="flex items-center gap-2 text-lg">
97
+ <svg className="h-5 w-5 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
98
+ <path
99
+ strokeLinecap="round"
100
+ strokeLinejoin="round"
101
+ strokeWidth={2}
102
+ d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
103
+ />
104
+ </svg>
105
+ Terminal UI
106
+ </CardTitle>
107
+ </CardHeader>
108
+ <CardContent className="text-muted-foreground">
109
+ Built-in TUI with syntax highlighting, file browser, and session management.
110
+ </CardContent>
111
+ </Card>
112
+
113
+ <Card>
114
+ <CardHeader>
115
+ <CardTitle className="flex items-center gap-2 text-lg">
116
+ <svg className="h-5 w-5 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
117
+ <path
118
+ strokeLinecap="round"
119
+ strokeLinejoin="round"
120
+ strokeWidth={2}
121
+ d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"
122
+ />
123
+ </svg>
124
+ Share Sessions
125
+ </CardTitle>
126
+ </CardHeader>
127
+ <CardContent className="text-muted-foreground">
128
+ Share your AI sessions with others via public links. Great for collaboration.
129
+ </CardContent>
130
+ </Card>
131
+
132
+ <Card>
133
+ <CardHeader>
134
+ <CardTitle className="flex items-center gap-2 text-lg">
135
+ <svg className="h-5 w-5 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
136
+ <path
137
+ strokeLinecap="round"
138
+ strokeLinejoin="round"
139
+ strokeWidth={2}
140
+ d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"
141
+ />
142
+ </svg>
143
+ Multiple Providers
144
+ </CardTitle>
145
+ </CardHeader>
146
+ <CardContent className="text-muted-foreground">
147
+ Support for OpenAI, Anthropic, Google, and many more AI providers.
148
+ </CardContent>
149
+ </Card>
150
+
151
+ <Card>
152
+ <CardHeader>
153
+ <CardTitle className="flex items-center gap-2 text-lg">
154
+ <svg className="h-5 w-5 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
155
+ <path
156
+ strokeLinecap="round"
157
+ strokeLinejoin="round"
158
+ strokeWidth={2}
159
+ d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"
160
+ />
161
+ </svg>
162
+ Open Source
163
+ </CardTitle>
164
+ </CardHeader>
165
+ <CardContent className="text-muted-foreground">
166
+ MIT licensed. Forked from OpenCode with gratitude. Build with the community.
167
+ </CardContent>
168
+ </Card>
169
+
170
+ <Card>
171
+ <CardHeader>
172
+ <CardTitle className="flex items-center gap-2 text-lg">
173
+ <svg className="h-5 w-5 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
174
+ <path
175
+ strokeLinecap="round"
176
+ strokeLinejoin="round"
177
+ strokeWidth={2}
178
+ d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"
179
+ />
180
+ </svg>
181
+ Privacy First
182
+ </CardTitle>
183
+ </CardHeader>
184
+ <CardContent className="text-muted-foreground">
185
+ Your data stays local. Self-host the backend or use enterprise features.
186
+ </CardContent>
187
+ </Card>
188
+ </div>
189
+ </div>
190
+
191
+ {/* Downloads Section */}
192
+ <div className="container mx-auto max-w-6xl px-4 py-16">
193
+ <h2 className="mb-12 text-center text-3xl font-bold">Download</h2>
194
+ <div className="mx-auto max-w-2xl">
195
+ <Card>
196
+ <CardHeader>
197
+ <CardTitle>Installation Options</CardTitle>
198
+ </CardHeader>
199
+ <CardContent className="space-y-4">
200
+ <div className="rounded-lg border bg-muted p-4">
201
+ <p className="mb-2 font-medium">Via Bun (Recommended)</p>
202
+ <code className="block rounded bg-background p-2 font-mono text-sm">bun add -g jonsoc</code>
203
+ </div>
204
+ <div className="rounded-lg border bg-muted p-4">
205
+ <p className="mb-2 font-medium">Via npm</p>
206
+ <code className="block rounded bg-background p-2 font-mono text-sm">npm install -g jonsoc</code>
207
+ </div>
208
+ <div className="rounded-lg border bg-muted p-4">
209
+ <p className="mb-2 font-medium">Via curl (Linux/macOS)</p>
210
+ <code className="block rounded bg-background p-2 font-mono text-sm">
211
+ curl -fsSL https://jonsoc.com/install | bash
212
+ </code>
213
+ </div>
214
+ <p className="text-center text-sm text-muted-foreground">
215
+ Supports: macOS (ARM64), Linux (x64), Windows (x64)
216
+ </p>
217
+ </CardContent>
218
+ </Card>
219
+ </div>
220
+ </div>
221
+
222
+ {/* Footer */}
223
+ <footer className="border-t py-8">
224
+ <div className="container mx-auto max-w-6xl px-4 text-center text-sm text-muted-foreground">
225
+ <p>Built with gratitude for the OpenCode team. MIT License.</p>
226
+ <p className="mt-2">
227
+ <a href={GITHUB_REPO} className="hover:text-primary hover:underline">
228
+ GitHub
229
+ </a>
230
+ {" • "}
231
+ <a href="/dashboard" className="hover:text-primary hover:underline">
232
+ Dashboard
233
+ </a>
234
+ </p>
235
+ </div>
236
+ </footer>
237
+ </div>
238
+ )
239
+ }
@@ -0,0 +1,148 @@
1
+ import { createFileRoute, useParams } from "@tanstack/react-router"
2
+ import { useQuery } from "convex/react"
3
+ import { api } from "@jonsoc/convex"
4
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
5
+ import { Skeleton } from "@/components/ui/skeleton"
6
+
7
+ export const Route = createFileRoute("/share/$id")({
8
+ component: ShareViewerPage,
9
+ })
10
+
11
+ function ShareViewerPage() {
12
+ const { id } = useParams({ from: "/share/$id" })
13
+ const shareData = useQuery(api.share.getPublic, { slug: id })
14
+
15
+ if (shareData === undefined) {
16
+ return (
17
+ <div className="container mx-auto max-w-4xl px-4 py-8">
18
+ <Card>
19
+ <CardHeader>
20
+ <Skeleton className="h-8 w-64" />
21
+ </CardHeader>
22
+ <CardContent className="space-y-4">
23
+ <Skeleton className="h-20 w-full" />
24
+ <Skeleton className="h-20 w-full" />
25
+ <Skeleton className="h-20 w-full" />
26
+ </CardContent>
27
+ </Card>
28
+ </div>
29
+ )
30
+ }
31
+
32
+ if (shareData === null) {
33
+ return (
34
+ <div className="container mx-auto max-w-4xl px-4 py-8">
35
+ <Card>
36
+ <CardHeader>
37
+ <CardTitle>Share Not Found</CardTitle>
38
+ </CardHeader>
39
+ <CardContent>
40
+ <p className="text-muted-foreground">This share link is invalid, has been deleted, or is not public.</p>
41
+ </CardContent>
42
+ </Card>
43
+ </div>
44
+ )
45
+ }
46
+
47
+ // Group data by type
48
+ const session = shareData.data.find((d: any) => d.type === "session")?.data
49
+ const messages = shareData.data
50
+ .filter((d: any) => d.type === "message")
51
+ .sort((a: any, b: any) => {
52
+ const aTime = a.data?.createdAt || 0
53
+ const bTime = b.data?.createdAt || 0
54
+ return aTime - bTime
55
+ })
56
+ const diffs = shareData.data.find((d: any) => d.type === "session_diff")?.data || []
57
+
58
+ return (
59
+ <div className="container mx-auto max-w-4xl px-4 py-8">
60
+ {/* Share Header */}
61
+ <Card className="mb-6">
62
+ <CardHeader>
63
+ <CardTitle>{session?.title || "Untitled Session"}</CardTitle>
64
+ </CardHeader>
65
+ <CardContent>
66
+ <div className="flex flex-wrap gap-4 text-sm text-muted-foreground">
67
+ <span>ID: {shareData.id}</span>
68
+ <span>Created: {new Date(shareData.createdAt).toLocaleString()}</span>
69
+ {shareData.model && <span>Model: {shareData.model}</span>}
70
+ </div>
71
+ </CardContent>
72
+ </Card>
73
+
74
+ {/* Messages */}
75
+ {messages.length > 0 && (
76
+ <Card className="mb-6">
77
+ <CardHeader>
78
+ <CardTitle>Conversation</CardTitle>
79
+ </CardHeader>
80
+ <CardContent className="space-y-4">
81
+ {messages.map((msg: any, idx: number) => (
82
+ <div
83
+ key={idx}
84
+ className={`rounded-lg border p-4 ${
85
+ msg.data?.role === "user"
86
+ ? "bg-muted ml-8"
87
+ : msg.data?.role === "assistant"
88
+ ? "bg-card mr-8"
89
+ : "bg-card"
90
+ }`}
91
+ >
92
+ <div className="mb-2 text-xs font-medium uppercase text-muted-foreground">
93
+ {msg.data?.role || "unknown"}
94
+ </div>
95
+ <div className="prose prose-sm max-w-none dark:prose-invert">{msg.data?.content || "[No content]"}</div>
96
+ </div>
97
+ ))}
98
+ </CardContent>
99
+ </Card>
100
+ )}
101
+
102
+ {/* File Changes */}
103
+ {diffs.length > 0 && (
104
+ <Card>
105
+ <CardHeader>
106
+ <CardTitle>File Changes ({diffs.length})</CardTitle>
107
+ </CardHeader>
108
+ <CardContent>
109
+ <ul className="space-y-2">
110
+ {diffs.map((diff: any, idx: number) => (
111
+ <li key={idx} className="flex items-center gap-2 text-sm">
112
+ <span
113
+ className={`inline-block h-2 w-2 rounded-full ${
114
+ diff.status === "added"
115
+ ? "bg-green-500"
116
+ : diff.status === "deleted"
117
+ ? "bg-red-500"
118
+ : diff.status === "modified"
119
+ ? "bg-yellow-500"
120
+ : "bg-gray-500"
121
+ }`}
122
+ />
123
+ <code className="rounded bg-muted px-1 py-0.5 text-xs">{diff.path}</code>
124
+ <span className="text-muted-foreground">({diff.status})</span>
125
+ </li>
126
+ ))}
127
+ </ul>
128
+ </CardContent>
129
+ </Card>
130
+ )}
131
+
132
+ {/* Footer */}
133
+ <div className="mt-8 text-center text-sm text-muted-foreground">
134
+ <p>
135
+ Shared via jonsoc •{" "}
136
+ <a
137
+ href="https://github.com/Noisemaker111/Jonsoc"
138
+ target="_blank"
139
+ rel="noopener noreferrer"
140
+ className="hover:text-primary hover:underline"
141
+ >
142
+ GitHub
143
+ </a>
144
+ </p>
145
+ </div>
146
+ </div>
147
+ )
148
+ }