feat(ai-project-board-view): add AI Project Space Status Board#520
Open
LukasHirt wants to merge 12 commits into
Open
feat(ai-project-board-view): add AI Project Space Status Board#520LukasHirt wants to merge 12 commits into
LukasHirt wants to merge 12 commits into
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
LukasHirt
force-pushed
the
ext/2026-07-07-ai-project-board-view
branch
from
July 20, 2026 14:30
459dd22 to
4f6ab26
Compare
Signed-off-by: Lukas Hirt <info@hirt.cz>
…sables/useLLM.ts` with the BYO-LLM pattern (same-origin check against `OCIS_URL`, `status` states, `complete()` posting to the proxy with bearer token); add `src/utils/lane.ts` (Lane type, label/icon metadata, regex-based fallback line parser); add `src/composables/useExcerpt.ts` (capped text excerpt fetch via `clientService.webdav.getFileContents`, skipped for binary/oversized files); add `src/composables/useBoardClassification.ts` (reads `useResourcesStore().activeResources`, builds batched per-file prompt, calls `useLLM().complete()` with JSON response format, parses/falls back to line parsing, defaults unmatched files to Draft, exposes `lanes`/`status`/`isClassifying`/`panelError`/`classify()`); update `src/index.ts` to read `applicationConfig.llm` and register the `folderView` extension against `app.files.folder-views.project-spaces` with `componentAttrs: () => ({ llmConfig })`.
Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
…ard.vue` (file icon, name, last-modified), `src/components/BoardLane.vue` (lane header with icon/label/count, scrollable card list), and `src/components/ProjectBoardView.vue` (the `FolderView.component` rendering the three lanes via ODS `oc-*` components and utility classes, loading/empty/error states, and a "Re-run classification" button wired to `useBoardClassification().classify()`); wire `ProjectBoardView` as the `component` in the `folderView` extension registered in `src/index.ts`. Signed-off-by: Lukas Hirt <info@hirt.cz>
…eExcerpt.spec.ts` (text excerpt fetched/truncated, binary/unsupported mime skipped, oversized file skipped), `tests/unit/useBoardClassification.spec.ts` (happy-path JSON classification, malformed-JSON fallback to line parsing, excerpt-omitted filename-only fallback, LLM error mapping to `panelError`, empty folder state), and `tests/unit/ProjectBoardView.spec.ts` (three lanes render, cards placed correctly, loading/error/empty states, re-run button behavior) following the `InsightsPanel.spec.ts` stub-and-mock pattern. Signed-off-by: Lukas Hirt <info@hirt.cz>
…in `tests/e2e/acceptance.spec.ts` with real Playwright coverage mirroring `web-app-file-comments/tests/e2e/fileComments.spec.ts` — create a project space, upload files, switch to the Status Board view mode, and assert the three lanes render with files placed into cards; also cover the re-run classification button updating lane placement after a new file lands. Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
…if present) for the extension Signed-off-by: Lukas Hirt <info@hirt.cz>
…matrix, and oCIS apps config Signed-off-by: Lukas Hirt <info@hirt.cz>
…s with other packages Bump @ownclouders/web-client, @ownclouders/web-pkg, @ownclouders/extension-sdk to 12.5.0, and vitest, vue, vue-router, vue-tsc, happy-dom, prettier to match versions used across the rest of the monorepo after rebasing onto main. Signed-off-by: Lukas Hirt <info@hirt.cz>
LukasHirt
force-pushed
the
ext/2026-07-07-ai-project-board-view
branch
from
July 20, 2026 14:34
4f6ab26 to
0b0812b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Project spaces render as a flat grid, so a manager can't tell which deliverables are draft, in review, or final without opening every folder.
Solution
Registers an alternate project-space folder view: a three-lane board (Draft / In Review / Final) built from the same file list already loaded. The LLM classifies each file's lane from its name plus a short excerpt, returned as structured
{fileId, lane}pairs. Degrades: no structured output → one lane-per-file text line, pattern-matched; no tool use → single classification pass on load, no re-run button; small context → excerpt dropped to filename only.Extension points
app.files.folder-views.project-spacesWhy ship this now
Managers today infer status from naming conventions alone; this surfaces it visually for free from data the view already fetches.
What was built
The extension is
web-app-ai-project-board-view, which adds an LLM-classified status board as an alternate view mode for project-space folders. Rather than the flat file grid, it groups the same resource list already loaded into the view into three lanes — Draft, In Review, Final — inferred from each file's name and a short content excerpt. It's registered insrc/index.tsas afolderViewextension; note that despite the spec targetingapp.files.folder-views.project-spaces, the implementation actually registers onapp.files.folder-views.folder(documented with rationale in the package README).Classification is driven by
composables/useBoardClassification.ts, which readsactiveResourcesfromuseResourcesStore(), batches up to 60 files per prompt, and asks the BYO-LLM endpoint (viauseLLM().complete(), configured throughapplicationConfig.llm) to return structured{fileId, lane}JSON.composables/useExcerpt.tssupplies a capped 2KB text excerpt per file over WebDAV (Range-header fetch), skipping folders, non-text mime types, and oversized files so filename-only classification kicks in when content isn't available. Presentation is split acrossBoardCard.vue(icon, name, last-modified),BoardLane.vue(header with icon/label/count plus a scrollable card list and empty state), andProjectBoardView.vue, which orchestrates loading/empty/error/cross-origin/unconfigured states, a truncation notice when the 60-file cap is hit, and a manual "Re-run classification" button.The design leans on graceful degradation rather than hard failure: if the model doesn't support structured output,
utils/lane.tsfalls back to a regex line-parser (laneFromKeyword/parseLaneLines) that reads"<name>: <lane>"-style text, and any file the classifier can't confidently place defaults to Draft.useLLM.tsgained aresponseFormatoption (wired toresponse_formatin the request body) to request JSON output where the endpoint supports it, reusing the existing same-origin check and bearer-token completion path from the BYO-LLM composable pattern. Unit coverage (55 tests) exercises the excerpt fetch/truncation boundaries, both JSON and malformed-JSON classification paths, the full LLM error-code-to-panelErrormapping, and all view states. The end-to-end Playwright test drives the real view-mode switch and lane placement for uploaded fixture files, then re-runs classification after a fourth upload — its LLM response is mocked by intercepting the proxy call and deriving a realistic non-JSON reply from the actual outgoing prompt, deliberately exercising the fallback line-parser instead of depending on real oCIS file IDs.Gate
Effort: M · 🤖 Generated by extctl