feat(launcher): implement force-sync and chronological download priority
Onsite operators can now manually trigger a full pre-sync of all location materials via a "Force Sync Location" button in the Launcher config. This ensures podium Macs have all content cached before an event starts. - Added trigger__force_location_sync to launcher session store. - Implemented force_location_sync() in background sync engine to perform recursive metadata fetches for all sessions in a location. - Optimized download queue with a 4-tier chronological sort: 1. Event/Location assets (Global) 2. Sessions by start time (Earliest first) 3. Presentations by start time (Sequential order) 4. File creation date (First-in fairness for on-time uploads) - Updated module and native integration documentation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
15
.ae_brief
15
.ae_brief
@@ -1,22 +1,15 @@
|
|||||||
# Aether Project Brief: aether_app_sveltekit
|
# Aether Project Brief: aether_app_sveltekit
|
||||||
**Last Updated:** 2026-02-09 22:03:56
|
**Last Updated:** 2026-05-21 22:25:05
|
||||||
**Current Agent:** mcp_agent
|
**Current Agent:** mcp_agent
|
||||||
|
|
||||||
## 🛠️ What I Just Did
|
## 🛠️ What I Just Did
|
||||||
Addressed multiple Svelte compiler warnings:
|
Implemented "Force Sync Location" feature. Optimized file download order with a 4-tier chronological sort (Global > Session > Presentation > Creation Date). Added UI button for onsite operators. Updated project documentation. Verified with npm run check.
|
||||||
1. Converted ~30 decorative labels to spans (a11y).
|
|
||||||
2. Applied Svelte 5 untrack() pattern to initial state from props.
|
|
||||||
3. Fixed CSS scoping for TipTap editor.
|
|
||||||
4. Added rel="noopener noreferrer" to external links.
|
|
||||||
5. Commited changes in two atomic batches.
|
|
||||||
|
|
||||||
## 🚧 Current Blockers
|
## 🚧 Current Blockers
|
||||||
None. Remaining svelte-check warnings (219) require more granular ID/for linking in complex forms.
|
None.
|
||||||
|
|
||||||
## ➡️ Exact Next Steps
|
## ➡️ Exact Next Steps
|
||||||
1. Granular fix for remaining 68 label/ID associations in address/person forms.
|
User to review changes. Ready for onsite testing/deployment.
|
||||||
2. Systematic application of untrack() for remaining state-from-props warnings.
|
|
||||||
3. Clean up unused TipTap CSS selectors identified by svelte-check.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
*Generated by ae_brief*
|
*Generated by ae_brief*
|
||||||
|
|||||||
@@ -169,8 +169,8 @@ per event via config.
|
|||||||
1. **Presenter checks in** — staff looks up their session(s) in Pres Mgmt
|
1. **Presenter checks in** — staff looks up their session(s) in Pres Mgmt
|
||||||
2. **File upload** — staff or presenter uploads file to the correct presenter/session record
|
2. **File upload** — staff or presenter uploads file to the correct presenter/session record
|
||||||
3. **File verification** — staff opens the file on a practice station to confirm it renders
|
3. **File verification** — staff opens the file on a practice station to confirm it renders
|
||||||
4. **Launcher sync** — file appears in the Launcher within the next polling cycle
|
4. **Launcher Sync** — file appears in the Launcher within the next polling cycle. Staff can use the **Force Sync Location** button in the Launcher config to immediately queue all files for the room's schedule.
|
||||||
5. **Presenter proceeds to room** — podium kiosk already has the file cached
|
5. **Presenter proceeds to room** — podium kiosk already has the file cached.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -280,6 +280,18 @@ For production onsite use, **Native mode on Mac laptops** is the target. The Ele
|
|||||||
app pre-caches all session files in the background so presentations open instantly without
|
app pre-caches all session files in the background so presentations open instantly without
|
||||||
a network round-trip at the moment of launch.
|
a network round-trip at the moment of launch.
|
||||||
|
|
||||||
|
### Download Priority & Room Readiness
|
||||||
|
|
||||||
|
To ensure the podium is ready for the day's first sessions, the Launcher sync engine uses a
|
||||||
|
4-tier chronological sorting priority:
|
||||||
|
|
||||||
|
1. **Global Assets:** Event and Location level files (branding, walk-in slides) are cached first.
|
||||||
|
2. **Session Schedule:** Files for the earliest sessions in the room are prioritized.
|
||||||
|
3. **Presentation Order:** Within a session block, speakers are prioritized by their scheduled start time.
|
||||||
|
4. **First-In Fairness:** When times are equal, older uploads are prioritized over late revisions.
|
||||||
|
|
||||||
|
Operators can manually trigger a full pre-sync of all location materials via the **Force Sync Location** button in the Launcher configuration (Sync Engine section).
|
||||||
|
|
||||||
### Native Mode — Electron App
|
### Native Mode — Electron App
|
||||||
|
|
||||||
- **Repo:** `~/OSIT_dev/aether_app_native_electron/`
|
- **Repo:** `~/OSIT_dev/aether_app_native_electron/`
|
||||||
|
|||||||
@@ -72,10 +72,15 @@ Files are stored persistently using their SHA-256 hash to prevent filename colli
|
|||||||
- **Filename:** `{hash}.file`
|
- **Filename:** `{hash}.file`
|
||||||
|
|
||||||
### 4.2 Background Sync (File Warming)
|
### 4.2 Background Sync (File Warming)
|
||||||
When a user navigates to a session in the Launcher UI, the `LauncherBackgroundSync` component:
|
When a user navigates to a session in the Launcher UI, the `LauncherBackgroundSync` component warms the cache for that specific session. To ensure full room readiness, a **Force Sync Location** trigger is available in the configuration UI.
|
||||||
1. Extracts all `event_file_id` values for that session.
|
|
||||||
2. Checks the native cache via `aetherNative.check_cache`.
|
1. **Metadata Fetch:** The system fetches all sessions, presentations, and presenters for the current location into the local database (Dexie).
|
||||||
3. Triggers background downloads for missing files via `aetherNative.download_to_cache`.
|
2. **Chronological Priority:** Missing files are added to the download queue and sorted to prioritize the event schedule:
|
||||||
|
- **Tier 1: Global Assets** — Event and Location level files (virtual time 0).
|
||||||
|
- **Tier 2: Session Schedule** — Earliest sessions are prioritized first.
|
||||||
|
- **Tier 3: Presentation Order** — Within a session, speakers are prioritized by their start time.
|
||||||
|
- **Tier 4: Integrity & Fairness** — Tie-breakers use `created_on` (oldest first) to ensure on-time uploads are cached before last-minute revisions.
|
||||||
|
3. **Download:** Triggers background downloads via `aetherNative.download_to_cache` sequentially to preserve network bandwidth and ensure file integrity.
|
||||||
|
|
||||||
### 4.3 Safe Handover (Launch Sequence)
|
### 4.3 Safe Handover (Launch Sequence)
|
||||||
When a user clicks "Open", the system follows a non-destructive sequence:
|
When a user clicks "Open", the system follows a non-destructive sequence:
|
||||||
|
|||||||
@@ -119,6 +119,9 @@ export interface LauncherSessState {
|
|||||||
trigger_reload__event_session_obj_li: string | null;
|
trigger_reload__event_session_obj_li: string | null;
|
||||||
trigger_reload__event_location_obj_id: string | null;
|
trigger_reload__event_location_obj_id: string | null;
|
||||||
trigger_reload__event_location_obj_li: string | null;
|
trigger_reload__event_location_obj_li: string | null;
|
||||||
|
|
||||||
|
trigger__force_location_sync: any;
|
||||||
|
|
||||||
trigger__ws_connect: any;
|
trigger__ws_connect: any;
|
||||||
trigger__ws_disconnect: any;
|
trigger__ws_disconnect: any;
|
||||||
}
|
}
|
||||||
@@ -254,6 +257,8 @@ export const launcher_sess_defaults: LauncherSessState = {
|
|||||||
trigger_reload__event_location_obj_id: null,
|
trigger_reload__event_location_obj_id: null,
|
||||||
trigger_reload__event_location_obj_li: null,
|
trigger_reload__event_location_obj_li: null,
|
||||||
|
|
||||||
|
trigger__force_location_sync: null,
|
||||||
|
|
||||||
trigger__ws_connect: null,
|
trigger__ws_connect: null,
|
||||||
trigger__ws_disconnect: null
|
trigger__ws_disconnect: null
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ae_loc } from '$lib/stores/ae_stores';
|
import { ae_loc } from '$lib/stores/ae_stores';
|
||||||
import { events_loc } from '$lib/stores/ae_events_stores';
|
import { events_loc, events_sess } from '$lib/stores/ae_events_stores';
|
||||||
import Launcher_Cfg_Section from './launcher_cfg_section.svelte';
|
import Launcher_Cfg_Section from './launcher_cfg_section.svelte';
|
||||||
import { Pause, Play, RefreshCw } from '@lucide/svelte';
|
import { CloudDownload, Pause, Play, RefreshCw } from '@lucide/svelte';
|
||||||
interface Props {
|
interface Props {
|
||||||
on_expand?: () => void;
|
on_expand?: () => void;
|
||||||
}
|
}
|
||||||
@@ -41,6 +41,16 @@ let { on_expand }: Props = $props();
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Force Sync: fetches all child metadata for the location to trigger pre-caching -->
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => ($events_sess.launcher.trigger__force_location_sync = true)}
|
||||||
|
class="btn btn-sm preset-filled-primary-500 hover:preset-filled-primary-600 mb-3 w-full transition-all"
|
||||||
|
title="Fetch all sessions, presentations, and presenters for this room to force-sync all files to this machine.">
|
||||||
|
<CloudDownload size="1.2em" class="mr-2" />
|
||||||
|
Force Sync Location
|
||||||
|
</button>
|
||||||
|
|
||||||
{#if $events_loc.launcher.sync_intervals}
|
{#if $events_loc.launcher.sync_intervals}
|
||||||
<div class="grid grid-cols-1 gap-3">
|
<div class="grid grid-cols-1 gap-3">
|
||||||
<!-- Technical Timers (Edit Mode Only) -->
|
<!-- Technical Timers (Edit Mode Only) -->
|
||||||
|
|||||||
@@ -248,6 +248,18 @@ $effect(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Force Sync Trigger: fetches ALL child metadata for ALL sessions in the room.
|
||||||
|
// WHY: by default, the launcher only warms presentation/presenter caches for the
|
||||||
|
// *selected* session to save bandwidth. Onsite operators use this trigger to
|
||||||
|
// force-fetch everything ahead of time so the file sync loop can pre-cache
|
||||||
|
// every slide deck for the entire day.
|
||||||
|
$effect(() => {
|
||||||
|
if ($events_sess.launcher.trigger__force_location_sync) {
|
||||||
|
$events_sess.launcher.trigger__force_location_sync = false;
|
||||||
|
force_location_sync();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API Refresh: Event
|
* API Refresh: Event
|
||||||
*/
|
*/
|
||||||
@@ -377,7 +389,40 @@ async function run_sync_cycle() {
|
|||||||
.anyOf(all_for_ids)
|
.anyOf(all_for_ids)
|
||||||
.toArray();
|
.toArray();
|
||||||
|
|
||||||
const cacheable_files = files.filter((file_obj) => !is_url_file(file_obj));
|
const cacheable_files = files.filter(
|
||||||
|
(file_obj) => !is_url_file(file_obj)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Optimized Chronological Sync Order
|
||||||
|
// 1. Prioritize Event/Location level files (Global assets).
|
||||||
|
// 2. Prioritize by Session start time (Urgent schedule priority).
|
||||||
|
// 3. Prioritize by Presentation start time (Within a session).
|
||||||
|
// 4. Prioritize by created_on (Oldest first — "Not penalizing" on-time uploads).
|
||||||
|
cacheable_files.sort((a, b) => {
|
||||||
|
const time_a_sess = a.event_session_start_datetime
|
||||||
|
? new Date(a.event_session_start_datetime).getTime()
|
||||||
|
: 0;
|
||||||
|
const time_b_sess = b.event_session_start_datetime
|
||||||
|
? new Date(b.event_session_start_datetime).getTime()
|
||||||
|
: 0;
|
||||||
|
if (time_a_sess !== time_b_sess) return time_a_sess - time_b_sess;
|
||||||
|
|
||||||
|
const time_a_pres = a.event_presentation_start_datetime
|
||||||
|
? new Date(a.event_presentation_start_datetime).getTime()
|
||||||
|
: 0;
|
||||||
|
const time_b_pres = b.event_presentation_start_datetime
|
||||||
|
? new Date(b.event_presentation_start_datetime).getTime()
|
||||||
|
: 0;
|
||||||
|
if (time_a_pres !== time_b_pres) return time_a_pres - time_b_pres;
|
||||||
|
|
||||||
|
const created_a = a.created_on
|
||||||
|
? new Date(a.created_on).getTime()
|
||||||
|
: 0;
|
||||||
|
const created_b = b.created_on
|
||||||
|
? new Date(b.created_on).getTime()
|
||||||
|
: 0;
|
||||||
|
return created_a - created_b;
|
||||||
|
});
|
||||||
|
|
||||||
sync_stats.total = cacheable_files.length;
|
sync_stats.total = cacheable_files.length;
|
||||||
let cached_count = 0;
|
let cached_count = 0;
|
||||||
@@ -554,6 +599,45 @@ async function refresh_location_config() {
|
|||||||
console.error('Sync: Location refresh FAILED:', err);
|
console.error('Sync: Location refresh FAILED:', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force Location Sync
|
||||||
|
* Fetches ALL sessions and their children (presentations, presenters, files)
|
||||||
|
* for the current location into Dexie.
|
||||||
|
*/
|
||||||
|
async function force_location_sync() {
|
||||||
|
const location_id = $events_slct.event_location_id;
|
||||||
|
if (!location_id) return;
|
||||||
|
|
||||||
|
if (log_lvl)
|
||||||
|
console.log(`Sync: FORCING full sync for location: ${location_id}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Fetch all sessions with all children (recursive loads handled by load_ae_obj_li)
|
||||||
|
await events_func.load_ae_obj_li__event_session({
|
||||||
|
api_cfg: $ae_api,
|
||||||
|
for_obj_type: 'event_location',
|
||||||
|
for_obj_id: location_id,
|
||||||
|
inc_presentation_li: true,
|
||||||
|
inc_presenter_li: true,
|
||||||
|
inc_file_li: true, // Also get session-level files
|
||||||
|
view: 'alt',
|
||||||
|
hidden: 'all',
|
||||||
|
try_cache: true,
|
||||||
|
log_lvl: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
if (log_lvl)
|
||||||
|
console.log(
|
||||||
|
'Sync: Force metadata fetch complete. File sync cycle will follow.'
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Trigger the file sync cycle immediately to start downloads
|
||||||
|
run_sync_cycle();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Sync: Force location sync FAILED:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Monitor Overlay: only shown in Native/App mode.
|
<!-- Monitor Overlay: only shown in Native/App mode.
|
||||||
|
|||||||
Reference in New Issue
Block a user