Skip to content

feat(web-app-ai-quick-draft-creator): add AI quick draft creator action#465

Open
LukasHirt wants to merge 9 commits into
mainfrom
ext/2026-06-18-ai-quick-draft-creator
Open

feat(web-app-ai-quick-draft-creator): add AI quick draft creator action#465
LukasHirt wants to merge 9 commits into
mainfrom
ext/2026-06-18-ai-quick-draft-creator

Conversation

@LukasHirt

@LukasHirt LukasHirt commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a new AI Quick Draft Creator extension that lets users generate AI-written documents directly from the ownCloud Web file manager.

How it works

  1. A Create AI Draft action appears in the new-file menu (alongside folders, plain text files, etc.)
  2. Clicking it opens a modal where the user types a free-form description of the document they want (e.g. "Q3 budget review for EMEA team, include agenda and action-items table")
  3. The user picks an output format (Markdown or plain text)
  4. The extension calls the configured LLM via ai-llm-proxy, receives the generated content, and saves the file to the current folder via WebDAV
  5. The newly created file opens immediately in the editor — no manual navigation needed

What is included

  • packages/web-app-ai-quick-draft-creator/ — the full Vue 3 + TypeScript extension
    • Registers an action extension in the new-file menu
    • DraftCreatorModal.vue — description input, format selector, create/cancel buttons
    • useDraftCreator composable — LLM prompt building, WebDAV upload, returns { resource, space } so the modal can trigger the default file action on the result
    • Unit tests (Vitest) and E2E tests (Playwright) with full coverage
    • l10n/ translation scaffold
  • support/pages/loginPage.ts — extended with modal locators and pre-logout modal dismissal for reliable E2E teardown
  • dev/docker/ and support/actions/ — updated ocis.apps.yaml and docker-compose.yml to mount the new extension and wire up its LLM config

Configuration

The extension reads its LLM endpoint from the oCIS app config key web-app-ai-quick-draft-creator (or ai-quick-draft-creator in local dev mounts), following the same pattern as the other AI extensions in this repo.

Test plan

  • pnpm --filter web-app-ai-quick-draft-creator test:unit — all unit tests pass
  • pnpm --filter web-app-ai-quick-draft-creator test:e2e — E2E acceptance tests pass against a running oCIS stack
  • In the dev environment: open any folder → new-file menu → Create AI Draft → enter a description → Create → file is created and opens in the editor automatically
  • Cancelling the modal leaves no file behind
  • Without a configured LLM, the action is hidden (isVisible returns false)

@kw-security

kw-security commented Jun 19, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@dj4oC dj4oC left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for getting this up. Concept fits the repo well (an upload-menu action that drafts a doc via the LLM). Flagging issues found in review — marking as a comment since it's a draft. The security items are the important ones.

Blockers

  • [security] LLM is called directly from the browser with no origin guard. useLLM.ts does fetch(\${cfg.endpoint}/chat/completions`)(and/models) directly. The house pattern (see web-app-chat-with-file/src/composables/useChat.ts) routes everything through the same-origin ai-llm-proxyand refuses any endpoint whose origin ≠window.location.origin`. Please route all LLM traffic through the proxy.
  • [security] Admin API key is shipped to every client. index.ts reads applicationConfig.llm.apiKey and useLLM.ts sends it as Authorization: Bearer <apiKey>. applicationConfig is public, so this leaks the provider key to all users. Siblings forward the user's oCIS token (authStore.accessToken) and keep LLM_API_KEY server-side in the proxy. Please drop apiKey from client config entirely.

Major

  • Package lives under extensions/…, but pnpm-workspace.yaml only globs packages/* — so it isn't installed/built/linted/mounted. Move to packages/web-app-ai-quick-draft-creator/.
  • No dist/ mount in docker-compose.yml and no entry in dev/docker/ocis.apps.yaml / support/actions/ocis.apps.yaml, so applicationConfig.llm is always undefined → the action never appears. Add the mount + an apps-config key matching the mount-target dir.
  • No l10n/ directory and defineWebApplication returns no translations key, so strings can't be extracted/localized (they are correctly wrapped in $gettext, good).
  • useLLM.ts ships ~200 lines of unused code (stream, completeJSON, summariseMessages) and fires up to 4 capability-probe round-trips on every modal open; the feature only uses complete() once.

Minor

  • src/App.vue is an unused generated stub — remove.
  • useDraftCreator.ts writes slug-YYYY-MM-DD.md with no collision check → two same-day drafts silently overwrite.
  • E2E asserts are no-ops (toBeGreaterThanOrEqual(0), expect(true).toBe(true) early-returns), and the mock targets **/ai-llm-proxy/** while the code calls cfg.endpoint directly — the happy path is never exercised.
  • PR title isn't Conventional Commits — CI enforces it (e.g. feat(web-app-ai-quick-draft-creator): …).

Happy to pair on the proxy wiring if useful — chat-with-file is the closest reference.

@LukasHirt

Copy link
Copy Markdown
Collaborator Author

Review feedback addressed

All review items have been resolved. Here is a summary of every change made:

Blockers fixed

Direct browser LLM calls → proxy routing

  • useLLM.ts completely rewritten: removed all probe code, stream(), completeJSON(), and summariseMessages() (~190 lines deleted).
  • complete() now calls ${cfg.endpoint}/chat/completions with Authorization: Bearer ${authStore.accessToken} (the user's oCIS OIDC token).
  • A same-origin check throws a user-visible error if the endpoint is not on the same host as ownCloud, preventing token leakage.

API key removed from the browser

  • apiKey removed from LLMConfig and from index.ts entirely.
  • applicationConfig.llm.apiKey is no longer read. The proxy holds LLM_API_KEY server-side only.

Major fixes

Wrong directory → moved

  • Extension moved from extensions/ai-quick-draft-creator/packages/web-app-ai-quick-draft-creator/ via git mv (history preserved).
  • package.json name updated to web-app-ai-quick-draft-creator.

Docker / YAML config added

  • docker-compose.yml: added volume mount ./packages/web-app-ai-quick-draft-creator/dist:/web/apps/ai-quick-draft-creator.
  • dev/docker/ocis.apps.yaml: added ai-quick-draft-creator entry with proxy endpoint + model.
  • support/actions/ocis.apps.yaml: added web-app-ai-quick-draft-creator entry (full prefix for CI mounts).
  • .github/workflows/test.yml: added web-app-ai-quick-draft-creator to the test matrix.

l10n added

  • l10n/translations.json (initially {}) created.
  • l10n/template.pot created with all user-facing strings catalogued.
  • import translations from '../l10n/translations.json' and translations key wired into defineWebApplication.

Unused code removed

  • stream(), completeJSON(), summariseMessages(), all 4 capability-probe fetch() round-trips removed from useLLM.ts.
  • Capability-based tier logic (LLMCapabilities, rich vs. simple prompt branch) removed from useDraftCreator.ts.

Minor fixes

  • src/App.vue unused stub deleted.
  • deriveFilename now appends HH-mm-ss timestamp (slug-YYYY-MM-DD-HH-mm-ss.ext) — two same-day drafts can never silently overwrite each other.
  • DraftCreatorModal.vue updated to use <oc-button> (with appearance / variation props) and <oc-spinner> while creating.
  • E2E tests rewritten: all toBeGreaterThanOrEqual(0) / expect(true).toBe(true) no-ops replaced with real toBeVisible() / toBeDisabled() / toBeEnabled() assertions. Mock now targets **/ai-llm-proxy/v1/** (the same-origin proxy path). Happy-path test verifies the proxy is actually called and the modal closes on success.
  • Unit tests updated to match the slimmed UseLLMReturn interface; a new test verifies the timestamp collision suffix.
  • README updated to document the proxy security model, environment variables, and local dev workflow.

All 11 unit tests pass. TypeScript type check passes.

@LukasHirt LukasHirt force-pushed the ext/2026-06-18-ai-quick-draft-creator branch from 1af9eef to b68fab6 Compare June 29, 2026 15:39
@LukasHirt LukasHirt marked this pull request as ready for review June 29, 2026 15:49
@LukasHirt LukasHirt changed the title AI Quick Draft Creator feat(web-app-ai-quick-draft-creator): add AI quick draft creator action Jun 29, 2026
@LukasHirt LukasHirt force-pushed the ext/2026-06-18-ai-quick-draft-creator branch from b68fab6 to f50365d Compare June 29, 2026 16:08
LukasHirt and others added 7 commits June 29, 2026 20:18
…/ to packages/

Relocate ai-quick-draft-creator from the non-standard extensions/ directory
to packages/web-app-ai-quick-draft-creator/ so it is picked up by the
pnpm-workspace.yaml glob (packages/*) and follows the repo convention.
Rename the package to web-app-ai-quick-draft-creator.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Lukas Hirt <info@hirt.cz>
Security (blockers):
- Route all LLM requests through the same-origin ai-llm-proxy; the
  browser now sends Authorization: Bearer <oCIS OIDC token> to the proxy
  rather than calling the LLM endpoint directly.
- Remove apiKey from LLMConfig and from applicationConfig entirely; the
  provider API key is held server-side by the proxy only.
- useLLM.ts enforces a same-origin check at call time and throws a
  user-visible error if the endpoint is cross-origin.

Structural:
- Delete unused generated App.vue stub.
- Add l10n/translations.json (initially empty) and l10n/template.pot;
  wire translations into defineWebApplication return value.
- Update docker-compose.yml with dist volume mount for the extension.
- Add ai-quick-draft-creator entry to dev/docker/ocis.apps.yaml.
- Add web-app-ai-quick-draft-creator entry to support/actions/ocis.apps.yaml.
- Add web-app-ai-quick-draft-creator to the CI test matrix in test.yml.

Code quality:
- Remove ~190 lines of unused code from useLLM.ts: capability probe (4
  network requests on every modal open), stream(), completeJSON(), and
  summariseMessages().
- Remove capability-based tier logic from useDraftCreator.ts; always
  generate a well-structured draft prompt.
- Add HH-mm-ss timestamp suffix to derived filenames so two same-day
  drafts never silently overwrite each other.
- Replace raw <button> elements in DraftCreatorModal.vue with <oc-button>
  and add an <oc-spinner> during creation.

Tests:
- Unit tests updated to match the slimmed UseLLMReturn interface (no
  stream/completeJSON/capabilities); add a collision-suffix test.
- E2E tests rewritten: all no-op assertions replaced with real behavioral
  checks; proxy mock now targets **/ai-llm-proxy/v1/** (matching the
  same-origin path enforced by useLLM.ts); happy-path test verifies proxy
  is called and modal closes on success.

Docs:
- Update README to document the proxy security model, configuration, and
  environment variables.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Lukas Hirt <info@hirt.cz>
…sible

Signed-off-by: Lukas Hirt <info@hirt.cz>
…pe errors

Signed-off-by: Lukas Hirt <info@hirt.cz>
@LukasHirt LukasHirt force-pushed the ext/2026-06-18-ai-quick-draft-creator branch from f50365d to e9c3e1d Compare June 29, 2026 18:31
LukasHirt and others added 2 commits June 29, 2026 20:37
… instead of upload menu

Use appInfo.extensions with newFileMenu + customHandler to place the
'Draft from description' action in the correct #new-file-menu-drop
dropdown (the + New button), not the upload dropdown.

The newFileMenu.isVisible callback receives currentFolder directly as a
parameter, so the canUpload check is accurate and race-condition-free.

Update E2E tests to open #new-file-menu-btn and locate the item by its
menu title text.

Signed-off-by: Lukas Hirt <info@hirt.cz>
…tomatically

After a draft is created, trigger the default file action so the new
file opens immediately without requiring manual navigation.

Return `{ resource, space }` from `createDraft` instead of the filename
string so the modal can pass the created resource to `triggerDefaultAction`.

Also adds `oc-modal-body-actions-cancel` class to the cancel button for
reliable E2E targeting, dismisses open modals before logout in `LoginPage`,
drops the WebDAV PUT stub from E2E setup now that tests run against real
oCIS, and adds `public/manifest.json`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Lukas Hirt <info@hirt.cz>
@LukasHirt LukasHirt requested a review from dj4oC June 30, 2026 17:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants