Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions extensions/naxer-12/superdocs-share-sheet-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
build/
.pub-cache/
.pub/
*.iml
.idea/
.vscode/
android/.gradle/
android/local.properties
ios/Pods/
ios/.symlinks/
ios/Flutter/Flutter.framework
ios/Flutter/Flutter.podspec
.DS_Store
36 changes: 36 additions & 0 deletions extensions/naxer-12/superdocs-share-sheet-app/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "4cf24164269a5ebf0c16a028a00727d0e77bbb05"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 4cf24164269a5ebf0c16a028a00727d0e77bbb05
base_revision: 4cf24164269a5ebf0c16a028a00727d0e77bbb05
- platform: android
create_revision: 4cf24164269a5ebf0c16a028a00727d0e77bbb05
base_revision: 4cf24164269a5ebf0c16a028a00727d0e77bbb05
- platform: ios
create_revision: 4cf24164269a5ebf0c16a028a00727d0e77bbb05
base_revision: 4cf24164269a5ebf0c16a028a00727d0e77bbb05
- platform: macos
create_revision: 4cf24164269a5ebf0c16a028a00727d0e77bbb05
base_revision: 4cf24164269a5ebf0c16a028a00727d0e77bbb05

# User provided section

# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
207 changes: 207 additions & 0 deletions extensions/naxer-12/superdocs-share-sheet-app/PROGRESS.md

Large diffs are not rendered by default.

71 changes: 71 additions & 0 deletions extensions/naxer-12/superdocs-share-sheet-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# SuperDocs Mobile

A Flutter app that lets you edit a document from your phone in three taps: share a file into the
app from Mail, Messages, or Files, type or dictate one instruction, review the AI's proposed edit,
and share the finished file back out. It's a thin client on top of SuperDocs' own API — this app
does no editing itself.

Built for SuperDocs' engineering round, assigned build: mobile share-sheet app.

## What it does

1. Share a document into the app (or open it and tap "New Edit").
2. Type or dictate one instruction — the OS keyboard's built-in mic covers dictation, no custom
speech-to-text needed.
3. The app uploads the document and starts the edit in the background — you can close the app here.
4. A background task polls for completion and notifies you when it's ready to review.
5. Review the change highlighted in a WebView, approve or reject.
6. Export the finished file and hand it straight to the OS share sheet to send onward.

## What SuperDocs features it uses

- `POST /v1/documents/upload-base64` — uploads the shared file and starts a session
- `POST /v1/chat/async` — sends the edit instruction as a background job
- `GET /v1/jobs/{job_id}` — polls job status, including the async/background-completion path
- `POST /v1/chat/{session_id}/approve` — the human-approval gate before anything is final
- `POST /v1/documents/export` — exports the approved edit back to a real file

## Platform status

**Android**: built and verified on a real device/emulator — the full flow above (share intent,
upload, edit, review, approve, export, background completion surviving the app being force-closed)
was run end to end against a live account.

**iOS**: not built. This needs a Share Extension target and a background-task registration that
require a full Xcode install and a signed-in Apple ID to build and test — genuinely blocked, not
skipped by choice. The project scaffold (`ios/`) exists so this can be picked up directly.

## How to run it

Requires the [Flutter SDK](https://docs.flutter.dev/get-started/install) and, for Android, the
Android SDK + an emulator or device.

```bash
flutter pub get
flutter run
```

On first launch, paste a SuperDocs API key (get one at [use.superdocs.app](https://use.superdocs.app)
or via the agent self-signup flow documented at [docs.superdocs.app](https://docs.superdocs.app)) —
stored locally via `flutter_secure_storage`, never hardcoded.

Running the tests:

```bash
flutter test
```

## Known limitations

- The in-app "New Edit" quick-action button (as opposed to sharing a real file in) uses a bundled
sample document rather than a real file picker, since that entry point's job is just to
demonstrate the flow, not replace share-intake.
- Background completion is proven to survive the app being force-closed at the OS level (confirmed
via Android's own job scheduler logs showing the poll task run to completion while closed), but a
full real-time test of the resulting notification firing for one specific job needs longer than a
single working session — Android's real minimum background-poll interval is about 15 minutes.

## Full build log

The complete, dated log of what was verified against the real API, every bug found and fixed, and
why — see `PROGRESS.md` and `TASK.md` in this folder.
184 changes: 184 additions & 0 deletions extensions/naxer-12/superdocs-share-sheet-app/TASK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# TASK.md — Orientation for any agent picking this up

**Read this file first, completely, before touching any branch's task doc.** It assumes nothing
about prior conversation — everything you need to start cold is here or linked from here.

## What this project is

A Flutter mobile app (iOS + Android) that lets someone edit a document from their phone in three
taps: share a file into the app from Mail/Messages/Files, type or say one instruction, review the
AI's proposed edit, and share the finished file back out. It's a client built **on top of**
SuperDocs' existing document-editing API (`docs.superdocs.app`) — this app does not do any editing
itself; it's a thin, mobile-native front end for SuperDocs' `/v1/chat` and `/v1/documents/*`
endpoints.

Full requirements source: `docs/superpowers/specs/2026-08-10-mobile-share-sheet-app-design.md` in
this repo — read it if you want the reasoning behind decisions below, but everything you need to
*build* is restated concretely in your branch's task doc in `docs/tasks/`.

Destination when done: a PR into `https://github.com/naxer-12/doctask-jainam-shah`, `extensions/` folder
(that's the private repo this eventually ships to — not this repo).

## Decided architecture (do not re-litigate without a strong reason — if you must change it, update this file and say why)

**Stack**: Flutter, bare workflow (not a fully-managed no-code builder — iOS Share Extensions and
true background execution need native platform access Flutter's managed mode doesn't expose).

**Platform focus, decided 2026-08-17: Android only, iOS explicitly deferred.** The `ios/` project
scaffold exists (generated by `flutter create`) and is left in place, but no further iOS work
(Share Extension, background-task registration, device/simulator testing) should happen until
this is revisited. Reason: this machine has no full Xcode install (only the command-line-tools
stub — `xcodebuild` isn't even resolvable), and getting one requires signing into the App Store
with a real Apple ID interactively, which isn't something a coding agent can do unattended. Android
has a full, working toolchain (SDK, a real AVD, Gradle) already set up on this machine and is where
all real device verification has actually happened. If you're picking this up with access to a Mac
that has full Xcode, the `ios/` half of `share-intake`/`background-engine`'s task docs is still the
right spec to build against — nothing about the plan changed, only what gets built next here.

**Flow**:
```
1. User shares a file into the app (OS share sheet) or opens the app and picks one.
2. App generates a session_id (a UUID) itself, then calls POST /v1/documents/upload-base64 with
{ filename, file_base64, session_id, return_html: true } -> gets back { session_id, html,
filename, chunks_count, version_id, persisted: true }.
3. User types or dictates one instruction on the instruction screen.
4. App calls POST /v1/chat/async with { session_id, message: <instruction> } -> gets { job_id }.
App can be closed here. The pending job_id is persisted locally.
5. A background task polls GET /v1/jobs/{job_id} until status is "awaiting_approval" or
"completed" (a job can reach "completed" directly with no approval step, if auto-approved).
A local notification fires when it changes.
6. User reopens the app, sees the review screen: result.document_changes.updated_html in a
WebView, changed sections highlighted, big Approve/Reject buttons pinned to the bottom.
7. Approve -> POST /v1/chat/{session_id}/approve { job_id, approved: true } -> poll again briefly.
8. POST /v1/documents/export { session_id, format: "docx" } -> binary file back, filename parsed
from the quoted `filename="..."` part of Content-Disposition.
9. App hands that file straight to the OS share sheet so the user can send it onward.
```

**Correction made 2026-08-10, after the first draft of this spec**: an earlier version of this
plan had the app call `/v1/chat/async` with a raw file every time. The documented request shape for
that endpoint takes a `session_id` (from step 2's upload call) plus the instruction — you do not
need to resend the document on every turn. Upload once per document, then chat by session_id.

## API contract — verified live, 2026-08-17

All previously-logged assumptions below are now **resolved**, not guessed. Verified by: fetching
the real `https://api.superdocs.app/openapi.json`, and driving a real account through the full
upload → edit → poll → approve/auto-complete → export loop with real HTTP calls. Corrections from
the original (unverified) draft are marked **CORRECTED**.

1. **Auth**: manual API key entry (`flutter_secure_storage`), `Authorization: Bearer <key>` header
— confirmed correct as originally assumed.
2. **`/v1/documents/upload-base64` does accept real file bytes directly** (`.pdf`, `.docx`, `.txt`,
`.rtf`, `.md`, `.html`, `.htm`, `.tex`, plus `.zip` LaTeX archives) and converts server-side —
the original assumption was right in substance.
- **CORRECTED — request field name**: the real field is **`file_base64`**, not
`document_html`. `filename` and `file_base64` are both required.
- **CORRECTED — session_id is NOT auto-generated.** Omitting `session_id` triggers a one-off,
non-persisted conversion (`"persisted": false`, no `session_id` in the response at all) — it
is **not** usable as the start of a chat flow. The client must generate its own `session_id`
(a UUID; the API requires it match `^[a-zA-Z0-9_\-\.]+$`) and pass it explicitly to get a
persisted document you can chat against.
- Real response with a client-supplied `session_id`: `{ "html": null|"...", "session_id": "...",
"filename": "...", "chunks_count": N, "version_id": "...", "page_setup": null, "persisted":
true }`. `html` is only populated if `return_html: true` is also sent.
3. **`/v1/chat/async`**: request shape `{ message, session_id, document_html: null }` confirmed
correct as originally assumed — `document_html` should stay omitted once a session holds the
document.
4. **`GET /v1/jobs/{job_id}`**:
- **CORRECTED — status enum**: real values are `pending`, **`in_progress`** (not
`processing`), `awaiting_approval`, `completed`, `failed`, `cancelled`.
- **CORRECTED — response shape**: the edit result is nested under **`result.document_changes`**,
not a top-level `document_changes` field. `result.document_changes.updated_html` and
`.chunk_diffs` are the real paths.
- **New finding, not previously logged**: a job can go straight from `pending` to `completed`
with `requires_approval: null` and `changes[].status: "auto_approved"` — not every edit stops
at `awaiting_approval`. Reproduced live on a real device (tapping Approve on such a job 400s
with `"Job is not awaiting approval"`) — **fixed** in `review_screen.dart`, which now checks
the job's actual status before deciding whether to call `approve()` at all.
5. **`POST /v1/chat/{session_id}/approve`**:
- **CORRECTED — missing required field**: the real request requires **`job_id`** (plus
`approved`); the original draft only sent `{ approved }`, which would 422 on a real call.
Optional `change_id`/`changes` array exist for the deferred per-chunk v2 flow.
6. **`POST /v1/documents/export`**: request shape `{ session_id, format }` confirmed correct.
`format` is a real enum: `docx`, `pdf`, `html`, `markdown`, `txt`, `doc`.
- **New finding**: the `Content-Disposition` response header carries both `filename="..."` and
an RFC 5987 `filename*=UTF-8''...` form on the same line — a naive split on `filename=` picks
up the second form's junk too. Parse only the quoted value.

Everything above is now fixed in `lib/api/superdocs_client.dart`, `lib/models/job_status_result.dart`,
`lib/screens/home_screen.dart`, `lib/background/background_service.dart`, and
`lib/screens/review_screen.dart` — see `PROGRESS.md`'s 2026-08-17 entry for the full list.

## The shared interface every branch except Foundation depends on

Branch 1 (`foundation`) builds this. Branches 2-4 can build against this exact signature as a stub
before Branch 1 merges, then swap in the real implementation with no code changes on their side.

```dart
// lib/api/superdocs_client.dart

class UploadResult {
final String sessionId;
final String html;
final String filename;
}

class ChatJobResult {
final String jobId;
final String sessionId;
final String status; // "pending" | "in_progress" | "awaiting_approval" | "completed" | "failed" | "cancelled"
}

class JobStatusResult {
final String status;
final String? updatedHtml; // from result.document_changes.updated_html -- present once
// status == "awaiting_approval" or "completed" (a job can reach
// "completed" directly, with no awaiting_approval step, if the
// edit was auto-approved -- handle both)
final List<String>? changedChunkIds; // from result.document_changes.chunk_diffs
}

class ExportResult {
final List<int> fileBytes;
final String filename;
final String contentType;
}

class SuperDocsClient {
Future<UploadResult> uploadDocument({required List<int> fileBytes, required String filename});
Future<ChatJobResult> startEdit({required String sessionId, required String instruction});
Future<JobStatusResult> getJobStatus({required String jobId});
Future<void> approve({required String sessionId, required String jobId, required bool approved});
Future<ExportResult> export({required String sessionId, required String format});
}
```

Exact HTTP request/response bodies for each method are in `docs/tasks/01-foundation.md`.

## Branch map

| Branch | Task doc | Depends on |
|---|---|---|
| `foundation` | `docs/tasks/01-foundation.md` | nothing — build first |
| `share-intake` | `docs/tasks/02-share-intake.md` | `SuperDocsClient` interface above (stub OK) |
| `background-engine` | `docs/tasks/03-background-engine.md` | `SuperDocsClient` interface + `share-intake`'s job-queue storage shape |
| `review-export` | `docs/tasks/04-review-export.md` | `SuperDocsClient` interface above (stub OK) |

Merge order: `foundation` first, always. The other three touch disjoint files and can be built as
three parallel git worktrees/branches in any order after that — see each task doc's "Owns" section
for the exact files, and check for overlap before merging if you're unsure.

## If you're resuming this project (a previous agent stopped, crashed, or ran out of context)

1. Read `PROGRESS.md` in this repo root — it has a dated log entry per work session saying what was
done and what was decided that isn't captured in this file yet.
2. Run `git log --oneline --all --graph` to see every branch's actual state — don't trust this file
alone if `PROGRESS.md` says something happened that isn't reflected here; `PROGRESS.md` is the
more current source for "what actually happened," this file is the more current source for "what
we decided to build."
3. Check which branches exist (`git branch -a`) against the branch map above. A branch that exists
but isn't merged to `main` is in-progress, not abandoned — read its own commits before assuming
you need to start it over.
4. If something in this file conflicts with a task doc in `docs/tasks/`, this file wins — the task
docs should be updated to match, and that mismatch itself is worth a `PROGRESS.md` entry.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml

analyzer:
exclude:
- build/**
- android/**
- ios/**
- web/**
- windows/**
- macos/**
- linux/**

linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
14 changes: 14 additions & 0 deletions extensions/naxer-12/superdocs-share-sheet-app/android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/

# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks
Loading