Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
cd40c13
Update dependencies and refactor notification service for improved ac…
alexcibotari May 14, 2026
2e9c0ae
Migrate user dialog and invite dialog to Spartan UI components, updat…
alexcibotari May 14, 2026
6dd4464
Refactor space and user dialogs to use Spartan UI components, update …
alexcibotari May 14, 2026
e6824b2
feat: migrate dialogs from Angular Material to Spartan UI
alexcibotari May 14, 2026
e69db6e
feat: implement file streaming to GCS and update upload handling
alexcibotari May 16, 2026
a9451f4
feat: implement translation import lock mechanism and draft generation
alexcibotari May 16, 2026
bc9a790
feat: enhance translation service with automatic draft generation aft…
alexcibotari May 17, 2026
4b674f3
feat: update webhook events to use unified 'changed' event for conten…
alexcibotari May 17, 2026
a9fb807
feat: implement change detection for asset, content, schema, and tran…
alexcibotari May 18, 2026
0601a0c
feat: add condition to generate draft files only if translations are …
alexcibotari May 19, 2026
08ae1be
feat: clarify draft generation execution point in contents import task
alexcibotari May 19, 2026
fbc11e9
feat: remove 'fromDate' field from export tasks and update related me…
alexcibotari May 19, 2026
10179f2
feat: enhance export dialog with reactive forms and dynamic asset/con…
alexcibotari May 19, 2026
6147a1f
feat: remove unused @lessify/angular-tools dependency from package.json
alexcibotari May 19, 2026
6ced221
feat: update layout of import dialog for improved usability
alexcibotari May 19, 2026
9e8d05f
feat: add h, q, f image transform params to asset CDN endpoint
alexcibotari May 19, 2026
4bd7dee
feat: update layout of import dialog for improved usability
alexcibotari May 20, 2026
1f5a2e6
feat: document CDN asset endpoint and query parameters for image tran…
alexcibotari May 20, 2026
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
3 changes: 3 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,15 @@ Detailed documentation lives in `docs/`. Read the relevant file when working on
|-------|------|----------------------|
| Domain concepts (Space, Content, Schema, Translation, Asset) | [docs/concepts.md](../docs/concepts.md) | Any new feature, onboarding |
| CDN caching, `cv` param, redirect logic, TTLs | [docs/cdn-caching.md](../docs/cdn-caching.md) | `functions/src/v1/cdn.ts`, public API |
| V1 API — all endpoints, routers, middleware, token permissions | [docs/v1-functions-api.md](../docs/v1-functions-api.md) | Any work in `functions/src/v1/` |
| Publish flow & cache invalidation | [docs/publish-flow.md](../docs/publish-flow.md) | Content/translation publish, tasks |
| Webhooks — events, payload, HMAC signing, logging | [docs/webhooks.md](../docs/webhooks.md) | `functions/src/webhooks.ts`, `webhook-utils.ts`, webhook UI |
| API token auth & permissions | [docs/auth-tokens.md](../docs/auth-tokens.md) | Middleware, token management, public API |
| Firebase billing & cost optimization | [docs/billing.md](../docs/billing.md) | Functions, Storage, cost analysis |
| Frontend architecture, routing, libs/ui | [docs/frontend-architecture.md](../docs/frontend-architecture.md) | Any Angular feature work |
| NgRx Signal stores, state patterns | [docs/frontend-state.md](../docs/frontend-state.md) | Adding/editing stores or components |
| User roles, route guards, UI permissions | [docs/frontend-permissions.md](../docs/frontend-permissions.md) | Auth, guards, user management |
| Spartan UI migration (checkbox, select, notifications) | [docs/spartan-ui-migration.md](../docs/spartan-ui-migration.md) | Migrating Material → Spartan, dialogs, forms |
| **Feature modules — Admin** | | |
| Admin overview (users, spaces, settings) | [docs/features/admin/overview.md](../docs/features/admin/overview.md) | Any admin feature |
| Admin → Users | [docs/features/admin/admin-users.md](../docs/features/admin/admin-users.md) | `features/admin/users/` |
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@ Thumbs.db
.runtimeconfig.json
firebase-export
firebase-export-*

#Superpowers
/docs/superpowers
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,15 @@ Detailed documentation lives in `docs/`. Read the relevant file when working on
|-------|------|----------------------|
| Domain concepts (Space, Content, Schema, Translation, Asset) | [docs/concepts.md](docs/concepts.md) | Any new feature, onboarding |
| CDN caching, `cv` param, redirect logic, TTLs | [docs/cdn-caching.md](docs/cdn-caching.md) | `functions/src/v1/cdn.ts`, public API |
| V1 API — all endpoints, routers, middleware, token permissions | [docs/v1-functions-api.md](docs/v1-functions-api.md) | Any work in `functions/src/v1/` |
| Publish flow & cache invalidation | [docs/publish-flow.md](docs/publish-flow.md) | Content/translation publish, tasks |
| Webhooks — events, payload, HMAC signing, logging | [docs/webhooks.md](docs/webhooks.md) | `functions/src/webhooks.ts`, `webhook-utils.ts`, webhook UI |
| API token auth & permissions | [docs/auth-tokens.md](docs/auth-tokens.md) | Middleware, token management, public API |
| Firebase billing & cost optimization | [docs/billing.md](docs/billing.md) | Functions, Storage, cost analysis |
| Frontend architecture, routing, libs/ui | [docs/frontend-architecture.md](docs/frontend-architecture.md) | Any Angular feature work |
| NgRx Signal stores, state patterns | [docs/frontend-state.md](docs/frontend-state.md) | Adding/editing stores or components |
| User roles, route guards, UI permissions | [docs/frontend-permissions.md](docs/frontend-permissions.md) | Auth, guards, user management |
| Spartan UI migration (checkbox, select, notifications) | [docs/spartan-ui-migration.md](docs/spartan-ui-migration.md) | Migrating Material → Spartan, dialogs, forms |
| **Feature modules — Admin** | | |
| Admin overview (users, spaces, settings) | [docs/features/admin/overview.md](docs/features/admin/overview.md) | Any admin feature |
| Admin → Users | [docs/features/admin/admin-users.md](docs/features/admin/admin-users.md) | `features/admin/users/` |
Expand Down
7 changes: 7 additions & 0 deletions docs/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ spaces/
{spaceId}/
contents/
{contentId}
history/
{historyId}
translations/
{translationId}
schemas/
Expand All @@ -130,4 +132,9 @@ spaces/
{taskId}
tokens/
{tokenId}
translations-history/
{historyId}
```

> When a Space is deleted, `firestoreService.recursiveDelete()` removes the space document and all nested subcollections in one call.
> When a Content document is deleted, `firestoreService.recursiveDelete()` removes the content document and its `history` subcollection. Child folder contents (sibling documents referencing the folder via `parentSlug`) are cascade-deleted via the `onContentDelete` trigger.
39 changes: 39 additions & 0 deletions docs/features/spaces/assets.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,45 @@ File/folder browser driven by `SpaceStore.assetPath`. Supports two layout modes
- `openImportDialog()` / `openExportDialog()` — creates Tasks for background processing
- Unsplash integration (if `unsplash_ui_enable` Remote Config flag is `true`) — opens `UnsplashAssetsSelectDialogComponent`

## CDN Asset Endpoint

```
GET /api/v1/spaces/:spaceId/assets/:assetId
```

No auth required (public). Responses are cached for 365 days (`Cache-Control: public, max-age=31536000`).

### Query Parameters

| Param | Type | Description |
|-------|------|-------------|
| `w` | integer > 0 | Target width in pixels |
| `h` | integer > 0 | Target height in pixels |
| `q` | integer 1–100 | Output quality (default: `85`). Applies to JPEG, WebP, AVIF. Ignored for PNG. |
| `f` | string | Output format: `webp`, `jpeg`, `png`, or `avif`. Converts the image to this format. |
| `download` | (flag) | Changes `Content-Disposition` from `inline` to `form-data`, forcing a browser download. |
| `thumbnail` | (flag) | For animated WebP/GIF: extracts the first frame before resizing. For video: extracts a frame with FFmpeg, then resizes with Sharp. |

### Resize Behaviour (`w` / `h`)

Sharp is called as `resize(width ?? null, height ?? null)` with its default `cover` fit:

| `w` | `h` | Behaviour |
|-----|-----|-----------|
| ✓ | — | Scale to width, height auto — aspect ratio preserved, no crop |
| — | ✓ | Scale to height, width auto — aspect ratio preserved, no crop |
| ✓ | ✓ | **`cover` crop** — resizes to fill the exact box, excess edges are cropped |
| — | — | No resize — only format/quality re-encoding if `f`/`q` provided |

### Special Cases

- **`image/svg+xml`** — always passed through; `w`/`h`/`f` are ignored.
- **Animated WebP or GIF without `thumbnail`** — passed through unchanged (Sharp cannot resize animated files).
- **Animated WebP or GIF with `thumbnail`** — first frame extracted, then `w`/`h`/`f` apply normally.
- **Video with `w` + `thumbnail`** — frame extracted via FFmpeg, then resized with Sharp; output defaults to `image/webp`.

> See [V1 Functions API — Asset resize combinations](../../v1-functions-api.md) for the full implementation detail.

## Image Preview

Assets of type `image/*` render previews using `NgOptimizedImage` with the custom `IMAGE_LOADER`. The loader appends `?w=<width>` to the CDN URL for responsive resizing and `&thumbnail=true` for animated files.
Expand Down
6 changes: 5 additions & 1 deletion docs/features/spaces/translations.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,15 @@ The main component is one of the most complex in the app. It renders a hierarchi

| Service | Purpose |
|---------|---------|
| `TranslationService` | CRUD + publish |
| `TranslationService` | CRUD + publish + publishDraft (called automatically after every write) |
| `TranslationHistoryService` | Load and display edit history per key |
| `LocaleService` | Load space locales |
| `TaskService` | Create import/export tasks |
| `TokenService` | Retrieve API token for CDN preview links |
| `TranslateService` | AI translation (Google Translate / DeepL) |
| `NotificationService` | Snackbar feedback |
| `PlatformService` | Platform detection (keyboard shortcuts differ per OS) |

## Draft Generation

Every write operation in `TranslationService` (create, update, updateId, updateLocale, delete) automatically chains a call to `translation-publishdraft` onCall after the Firestore write succeeds. This keeps the draft Storage files (`draft/{locale}.json`) in sync without a Firestore trigger.
4 changes: 2 additions & 2 deletions docs/frontend-architecture.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Frontend Architecture

> Related: [State Management](frontend-state.md) · [User Roles & Permissions](frontend-permissions.md) · [Concepts](concepts.md)
> Related: [State Management](frontend-state.md) · [User Roles & Permissions](frontend-permissions.md) · [Concepts](concepts.md) · [Spartan UI Migration](spartan-ui-migration.md)

## Tech Stack

| Layer | Technology |
|-------|-----------|
| Framework | Angular 21 (standalone, zoneless, signals) |
| State | NgRx Signals (`@ngrx/signals`) |
| UI Components | Angular Material + custom Spartan/Helm (`libs/ui/`) |
| UI Components | Angular Material (being migrated) + Spartan/Helm (`libs/ui/`) |
| Styling | Tailwind CSS 4 + SCSS |
| Backend SDK | AngularFire (Firestore, Auth, Storage, Functions, Remote Config) |
| Rich Text | TipTap editor |
Expand Down
47 changes: 42 additions & 5 deletions docs/publish-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,50 @@ Draft files are separate from published files. Consumers must pass `?version=dra

```
1. User clicks "Publish Translations" in the UI
2. Function reads all Translation documents from Firestore for the Space
3. Function groups by locale, writes flat key/value JSON to Storage:
2. Angular calls Firebase Function (translation-publish onCall)
3. Function reads all Translation documents from Firestore for the Space
4. Function groups by locale, writes flat key/value JSON to Storage:
spaces/{spaceId}/translations/{locale}.json (one per locale)
4. Function updates the cache marker:
5. Function updates the cache marker:
spaces/{spaceId}/translations/cache.json (new generation = new cv)
```

---

## Translation Draft Flow

Draft JSON files are kept in sync so consumers can preview unpublished changes via `?version=draft`.

### Frontend saves (add / edit / rename / delete)

```
1. User saves a translation key in the UI
2. Angular TranslationService writes to Firestore
3. On success, TranslationService calls translation-publishdraft onCall
4. Function reads all translations and writes draft JSON to Storage:
spaces/{spaceId}/translations/draft/{locale}.json
```

### Import Task (TRANSLATION_IMPORT / TRANSLATION_IMPORT_FLAT)

```
1. Task Function processes all rows via BulkWriter
2. After bulk.close(), Function calls generateTranslationsDraft() once
3. Draft files written for all locales in a single pass
```

### CLI Manage API (POST /api/v1/spaces/:spaceId/translations/:locale)

```
1. Manage endpoint processes all rows via BulkWriter
2. After bulk.close(), endpoint calls generateTranslationsDraft() once
3. Draft files written for all locales in a single pass
```

> **Note:** There is no Firestore trigger watching translation writes. Draft generation is always triggered explicitly — either by the frontend calling `translation-publishdraft` or by the import/manage flow calling `generateTranslationsDraft()` directly at the end.

---

## Cache Invalidation

The `cache.json` file is a **cache pointer** — its content is mostly irrelevant, but its Firebase Storage **generation number** is used as the `cv` (cache version).
Expand Down Expand Up @@ -96,6 +131,8 @@ Resolution is done at request time by reading additional Storage files. This add
## Implementation Files

- `functions/src/contents.ts` — publish content Firebase Function
- `functions/src/translations.ts` — publish translations Firebase Function
- `functions/src/translations.ts` — `publish` onCall, `publishDraft` onCall, `onWriteToHistory` trigger
- `functions/src/services/content.service.ts` — `contentLocaleCachePath`, `spaceContentCachePath`
- `functions/src/services/translation.service.ts` — `translationLocaleCachePath`, `spaceTranslationCachePath`
- `functions/src/services/translation.service.ts` — `saveTranslationFiles`, `generateTranslationsDraft`, `translationLocaleCachePath`, `spaceTranslationCachePath`
- `functions/src/tasks.ts` — `translationsImport`, `translationsImportJsonFlat` (call `generateTranslationsDraft` at end)
- `functions/src/v1/manage.ts` — CLI push endpoint (calls `generateTranslationsDraft` at end)
Loading