Merge branch 'master' into dev

Resolved conflicts:
- adb.ts: keep both goToMachineHome() (dev) and executeStreamingCmd() (master)
- messageHandler.ts / ws_messageSender.ts: adopt master's semver.satisfies
  version check (forward-correct vs startsWith('0.0.2')); keep dev's additive
  bits (raw_stream_end_priceslot handler, ciphertext/iv guard) + master's
  (announce handler, 'upload-log' command); drop now-unused isSecuredAppVersion
- AnnouncementDialog.svelte: drop invalid export on interface (Svelte 5)
- install @types/semver + semver (bun.lock)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
thanawat saiyota 2026-06-30 17:55:37 +07:00
commit 7a406fd409
20 changed files with 2615 additions and 57 deletions

View file

@ -18,10 +18,22 @@
} from '@yume-chan/adb-daemon-webusb';
import AdbWebCredentialStore from '@yume-chan/adb-credential-web';
import { deviceCredentialManager } from '$lib/core/adb/deviceCredManager';
import { browser } from '$app/environment';
import { onMount } from 'svelte';
let { children } = $props();
let websocketConnectedForUid = $state('');
let adbReconnectTriedForUid = $state('');
let TerminalDrawerComponent: any = $state(null);
// Dynamic import: TerminalDrawer depends on @xterm/xterm which references
// browser globals (self, window). Static import would crash SSR evaluation.
onMount(async () => {
if (browser) {
const mod = await import('$lib/components/terminal-drawer.svelte');
TerminalDrawerComponent = mod.default;
}
});
function getAutoConnectChannel(pathname: string) {
if (pathname.startsWith('/tools/create-menu')) {
@ -125,4 +137,8 @@
<Sidebar.Trigger />
{@render children()}
</main>
{#if TerminalDrawerComponent}
<svelte:component this={TerminalDrawerComponent} />
{/if}
</Sidebar.Provider>

View file

@ -37,11 +37,16 @@
clearMenuSaveState
} from '$lib/core/stores/menuSaveStore';
import * as semver from 'semver';
import { GlobalEventBus } from '$lib/core/utils/eventBus';
const sourceDir = '/sdcard/coffeevending';
const stagedMenuStorageKey = 'brew.create-menu.drafts.v1';
const deletedStagedMenuStorageKey = `${stagedMenuStorageKey}.deleted`;
const stagedMenuAndroidPath = `${sourceDir}/cfg/supra_draft_menus.json`;
const APP_VERSION = env.PUBLIC_APP_SEMVER;
// fetched recipe
let devRecipe: any | undefined = $state();
@ -63,6 +68,32 @@
let isAndroidSocketConnected = $derived(Boolean($adbWriter));
let isRecipeLoaded = $derived(Boolean(devRecipe));
// clear out event
GlobalEventBus.on('recipe-event', (d: any) => {
console.log('[recipe-ev] get event: ', d);
if (d?.type == 'load-recipe' && d?.status == 'end') {
addNotification('INFO:Get data, waiting for reloading ...');
// load finish
//\
let recipeRaw = get(recipeFromMachine);
if (recipeRaw) {
devRecipe = recipeRaw;
// update material & topping
console.log('check dev recipe', devRecipe);
}
// data.recipes = r01Q.recipe;
buildOverviewForBrewing();
console.log('refresh by m2 mem recipe data done');
recipeLoading = false;
addNotification('INFO:Load recipe from memories success!');
}
});
async function pullTextWithRetry(path: string, timeoutMs = 15000, attempts = 2) {
for (let attempt = 1; attempt <= attempts; attempt++) {
const content = await adb.pull(path, timeoutMs);
@ -82,35 +113,51 @@
console.log('check instance', instance);
if (instance) {
recipeLoading = true;
try {
console.log('instance passed!');
const recipePaths = [
`${sourceDir}/cfg/recipe_branch_dev.json`,
`${sourceDir}/coffeethai02.json`
];
for (const recipePath of recipePaths) {
const dev_recipe = await pullTextWithRetry(recipePath);
console.log('dev recipe pull result', {
recipePath,
loaded: dev_recipe != undefined,
size: dev_recipe?.length ?? 0
if (semver.satisfies(APP_VERSION, '^0.0.3')) {
try {
addNotification('WARN:Load recipe from app memories ...');
sendToAndroid({
type: 'get_recipe',
payload: {}
});
if (!dev_recipe || dev_recipe.trim().length == 0) continue;
try {
devRecipe = JSON.parse(dev_recipe);
buildOverviewForBrewing();
return;
} catch (error) {
console.error('failed to parse recipe json', recipePath, error);
addNotification(`ERROR:Invalid recipe JSON from ${recipePath}`);
}
// GlobalEventBus.emit('recipe-event', 'wait-finish');
} finally {
recipeLoading = false;
}
} else {
try {
console.log('instance passed!');
const recipePaths = [
`${sourceDir}/cfg/recipe_branch_dev.json`,
`${sourceDir}/coffeethai02.json`
];
addNotification('ERROR:Cannot fetch recipe from machine');
} finally {
recipeLoading = false;
for (const recipePath of recipePaths) {
const dev_recipe = await pullTextWithRetry(recipePath);
console.log('dev recipe pull result', {
recipePath,
loaded: dev_recipe != undefined,
size: dev_recipe?.length ?? 0
});
if (!dev_recipe || dev_recipe.trim().length == 0) continue;
try {
devRecipe = JSON.parse(dev_recipe);
buildOverviewForBrewing();
return;
} catch (error) {
console.error('failed to parse recipe json', recipePath, error);
addNotification(`ERROR:Invalid recipe JSON from ${recipePath}`);
}
}
addNotification('ERROR:Cannot fetch recipe from machine');
} finally {
recipeLoading = false;
}
}
} else {
addNotification('ERROR:Cannot connect to machine');