kanna-code 0.26.3 → 0.26.4

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.
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <link rel="icon" type="image/png" href="/favicon.png" />
7
7
  <title>Kanna</title>
8
- <script type="module" crossorigin src="/assets/index-QXLrswKX.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-DDI3pFzk.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-C5RBxQW4.css">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kanna-code",
3
3
  "type": "module",
4
- "version": "0.26.3",
4
+ "version": "0.26.4",
5
5
  "description": "A beautiful web UI for Claude Code",
6
6
  "license": "MIT",
7
7
  "keywords": [
@@ -1,5 +1,5 @@
1
1
  import { afterEach, describe, expect, test } from "bun:test"
2
- import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"
2
+ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"
3
3
  import { tmpdir } from "node:os"
4
4
  import path from "node:path"
5
5
  import { appendGitIgnoreEntry, DiffStore, extractGitHubRepoSlug, fetchGitHubPullRequests } from "./diff-store"
@@ -273,6 +273,27 @@ describe("DiffStore", () => {
273
273
  expect(await readFile(path.join(repoRoot, ".gitignore"), "utf8")).toBe("scratch.log\n")
274
274
  })
275
275
 
276
+ test("ignoreFile accepts a folder entry for an untracked diff", async () => {
277
+ const repoRoot = await createRepo()
278
+ tempDirs.push(repoRoot)
279
+ await writeFile(path.join(repoRoot, "tracked.txt"), "base\n", "utf8")
280
+ await run(["git", "add", "."], repoRoot)
281
+ await run(["git", "commit", "-m", "init"], repoRoot)
282
+ await mkdir(path.join(repoRoot, "tmp/cache"), { recursive: true })
283
+ await writeFile(path.join(repoRoot, "tmp/cache/output.log"), "tmp\n", "utf8")
284
+
285
+ const store = new DiffStore(repoRoot)
286
+ await store.initialize()
287
+ await store.refreshSnapshot("project-1", repoRoot)
288
+ await store.ignoreFile({
289
+ projectId: "project-1",
290
+ projectPath: repoRoot,
291
+ path: "tmp/cache/",
292
+ })
293
+
294
+ expect(await readFile(path.join(repoRoot, ".gitignore"), "utf8")).toBe("tmp/cache/\n")
295
+ })
296
+
276
297
  test("appendGitIgnoreEntry does not duplicate an existing identical entry", () => {
277
298
  expect(appendGitIgnoreEntry("scratch.log\n", "scratch.log")).toBe("scratch.log\n")
278
299
  expect(appendGitIgnoreEntry("scratch.log", "scratch.log")).toBe("scratch.log\n")
@@ -2103,23 +2103,26 @@ export class DiffStore {
2103
2103
  projectPath: string
2104
2104
  path: string
2105
2105
  }) {
2106
- const relativePath = normalizeRepoRelativePath(args.path)
2106
+ const ignoreEntry = normalizeRepoRelativePath(args.path)
2107
2107
  const repo = await resolveRepo(args.projectPath)
2108
2108
  if (!repo) {
2109
2109
  throw new Error("Project is not in a git repository")
2110
2110
  }
2111
2111
 
2112
- const entry = await findDirtyPath(repo.repoRoot, relativePath)
2113
- if (!entry) {
2114
- throw new Error(`File is no longer changed: ${relativePath}`)
2115
- }
2116
- if (!entry.isUntracked) {
2112
+ const dirtyPaths = await listDirtyPaths(repo.repoRoot)
2113
+ const exactEntry = dirtyPaths.find((candidate) => candidate.path === ignoreEntry)
2114
+ if (exactEntry && !exactEntry.isUntracked) {
2117
2115
  throw new Error("Only untracked files can be ignored from the diff viewer")
2118
2116
  }
2119
2117
 
2118
+ const entry = dirtyPaths.find((candidate) => candidate.isUntracked && (candidate.path === ignoreEntry || candidate.path.startsWith(ignoreEntry)))
2119
+ if (!entry) {
2120
+ throw new Error(`File is no longer changed: ${ignoreEntry}`)
2121
+ }
2122
+
2120
2123
  const gitignorePath = path.join(repo.repoRoot, ".gitignore")
2121
2124
  const currentContents = await readFile(gitignorePath, "utf8").catch(() => null)
2122
- const nextContents = appendGitIgnoreEntry(currentContents, relativePath)
2125
+ const nextContents = appendGitIgnoreEntry(currentContents, ignoreEntry)
2123
2126
  if (nextContents !== currentContents) {
2124
2127
  await writeFile(gitignorePath, nextContents, "utf8")
2125
2128
  }
@@ -15,8 +15,8 @@ import { createWsRouter, type ClientState } from "./ws-router"
15
15
  import { deleteProjectUpload, inferAttachmentContentType, inferProjectFileContentType, persistProjectUpload } from "./uploads"
16
16
  import { getProjectUploadDir } from "./paths"
17
17
 
18
- const MAX_UPLOAD_FILES = 10
19
- const MAX_UPLOAD_SIZE_BYTES = 25 * 1024 * 1024
18
+ const MAX_UPLOAD_FILES = 50
19
+ const MAX_UPLOAD_SIZE_BYTES = 100 * 1024 * 1024
20
20
  const STALE_EMPTY_CHAT_PRUNE_INTERVAL_MS = 60 * 1000
21
21
 
22
22
  export async function persistUploadedFiles(args: {
@@ -182,7 +182,7 @@ describe("uploads", () => {
182
182
  try {
183
183
  const project = await server.store.openProject(projectDir, "Project")
184
184
  const formData = new FormData()
185
- formData.append("files", new File([new Uint8Array(25 * 1024 * 1024 + 1)], "big.bin", { type: "application/octet-stream" }))
185
+ formData.append("files", new File([new Uint8Array(100 * 1024 * 1024 + 1)], "big.bin", { type: "application/octet-stream" }))
186
186
 
187
187
  const response = await fetch(`http://localhost:${server.port}/api/projects/${project.id}/uploads`, {
188
188
  method: "POST",
@@ -191,7 +191,7 @@ describe("uploads", () => {
191
191
 
192
192
  expect(response.status).toBe(413)
193
193
  expect(await response.json()).toEqual({
194
- error: "File \"big.bin\" exceeds the 25 MB limit.",
194
+ error: "File \"big.bin\" exceeds the 100 MB limit.",
195
195
  })
196
196
  } finally {
197
197
  await server.stop()