ikanban-web 0.2.5 → 0.2.7

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/README.md CHANGED
@@ -1,34 +1,39 @@
1
- ## Usage
1
+ # ikanban
2
2
 
3
- Dependencies for these templates are managed with [pnpm](https://pnpm.io) using `pnpm up -Lri`.
3
+ iKanban is a Web interface for the [OpenCode](https://opencode.ai) AI coding agent. It unifies chat, terminal, and board workflows in a single interface so teams can plan, execute, and track coding tasks.
4
4
 
5
- This is the reason you see a `pnpm-lock.yaml`. That said, any package manager will work. This file can safely be removed once you clone a template.
5
+ ## UI Previews
6
6
 
7
- ```bash
8
- $ npm install # or pnpm install or yarn install
9
- ```
7
+ <details>
8
+ <summary>Expand screenshots</summary>
10
9
 
11
- ### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
10
+ <img width="3695" height="2149" alt="Image" src="https://github.com/user-attachments/assets/b5ca21b8-b8aa-46f6-84cc-8906f629b6c8" />
12
11
 
13
- ## Available Scripts
12
+ </details>
14
13
 
15
- In the project directory, you can run:
14
+ ## Quick Start
16
15
 
17
- ### `npm run dev` or `npm start`
16
+ ### Option 1: Use the hosted app (recommended)
18
17
 
19
- Runs the app in the development mode.<br>
20
- Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
18
+ Open: https://isomoes.github.io/ikanban
21
19
 
22
- The page will reload if you make edits.<br>
20
+ Start OpenCode with CORS enabled for GitHub Pages:
23
21
 
24
- ### `npm run build`
22
+ ```bash
23
+ opencode serve --port <PORT> --cors https://isomoes.github.io
24
+ ```
25
25
 
26
- Builds the app for production to the `dist` folder.<br>
27
- It correctly bundles Solid in production mode and optimizes the build for the best performance.
26
+ Then add your server URL in settings: `http://localhost:<PORT>`.
28
27
 
29
- The build is minified and the filenames include the hashes.<br>
30
- Your app is ready to be deployed!
28
+ ### Option 2: Run locally with npx
29
+
30
+ ```bash
31
+ npx ikanban-web@latest # Start on port 3000
32
+ npx ikanban-web@latest --port 8080 # Custom port
33
+ OPENCODE_URL=http://myserver:4096 npx ikanban-web@latest # External OpenCode server
34
+ ```
31
35
 
32
- ## Deployment
36
+ ## Acknowledgments
33
37
 
34
- You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.)
38
+ - Inspired by and thanks to the [openchamber](https://github.com/btriapitsyn/openchamber) project.
39
+ - [opencode web UI](https://github.com/anomalyco/opencode/tree/dev/packages/app)
package/bin/cli.js CHANGED
@@ -30,8 +30,9 @@ const PORT = portFlag !== -1 ? parseInt(args[portFlag + 1], 10) : 3000
30
30
  // OpenCode backend: honour OPENCODE_URL or default to localhost:4096
31
31
  const OPENCODE_URL = process.env.OPENCODE_URL || "http://localhost:4096"
32
32
 
33
- function proxyRequest(req, res) {
33
+ function proxyRequest(req, res, overridePath) {
34
34
  const target = new URL(req.url, OPENCODE_URL)
35
+ if (overridePath !== undefined) target.pathname = overridePath
35
36
  const mod = target.protocol === "https:" ? https : http
36
37
  const options = {
37
38
  hostname: target.hostname,
@@ -51,9 +52,18 @@ function proxyRequest(req, res) {
51
52
  req.pipe(proxy)
52
53
  }
53
54
 
55
+ const BASE = "/ikanban"
56
+
54
57
  const server = http.createServer((req, res) => {
55
58
  const url = new URL(req.url, `http://localhost:${PORT}`)
56
59
 
60
+ // Redirect bare /ikanban to /ikanban/
61
+ if (url.pathname === BASE) {
62
+ res.writeHead(301, { Location: BASE + "/" })
63
+ res.end()
64
+ return
65
+ }
66
+
57
67
  // Proxy OpenCode API paths to backend.
58
68
  // These are the top-level path segments used by @opencode-ai/sdk.
59
69
  const API_PREFIXES = [
@@ -82,15 +92,37 @@ const server = http.createServer((req, res) => {
82
92
  "/tui",
83
93
  "/vcs",
84
94
  ]
95
+ // Support both /session/... (SDK default, same-origin) and
96
+ // /ikanban/session/... (prefixed, e.g. from a reverse proxy).
97
+ // Determine the effective API pathname by stripping the base prefix if present.
98
+ let apiPathname = url.pathname
99
+ if (apiPathname.startsWith(BASE + "/")) {
100
+ const stripped = apiPathname.slice(BASE.length)
101
+ if (API_PREFIXES.some((p) => stripped === p || stripped.startsWith(p + "/"))) {
102
+ apiPathname = stripped
103
+ }
104
+ }
85
105
  const isApi = API_PREFIXES.some(
86
- (p) => url.pathname === p || url.pathname.startsWith(p + "/"),
106
+ (p) => apiPathname === p || apiPathname.startsWith(p + "/"),
87
107
  )
88
108
  if (isApi) {
89
- return proxyRequest(req, res)
109
+ // Pass the stripped path so the backend never sees the /ikanban prefix
110
+ return proxyRequest(req, res, apiPathname !== url.pathname ? apiPathname : undefined)
111
+ }
112
+
113
+ // Strip /ikanban prefix before resolving static files
114
+ let pathname = url.pathname
115
+ if (pathname.startsWith(BASE + "/")) {
116
+ pathname = pathname.slice(BASE.length) || "/"
117
+ } else if (pathname !== BASE) {
118
+ // Anything outside /ikanban/ that isn't an API call gets a 404
119
+ res.writeHead(404)
120
+ res.end("Not found")
121
+ return
90
122
  }
91
123
 
92
124
  // Serve static files from dist/
93
- let filePath = path.join(DIST, url.pathname)
125
+ let filePath = path.join(DIST, pathname)
94
126
  if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) {
95
127
  filePath = path.join(DIST, "index.html")
96
128
  }
@@ -110,7 +142,7 @@ const server = http.createServer((req, res) => {
110
142
  })
111
143
 
112
144
  server.listen(PORT, () => {
113
- console.log(`iKanban running at http://localhost:${PORT}`)
145
+ console.log(`iKanban running at http://localhost:${PORT}${BASE}/`)
114
146
  console.log(`OpenCode backend: ${OPENCODE_URL}`)
115
147
  console.log("Press Ctrl+C to stop")
116
148
  })