Skip to content
Draft
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
95 changes: 95 additions & 0 deletions src/apps/mobile/design-system/components/mobile-components.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,101 @@
"states": ["loading", "text", "markdown", "image", "unsupported", "failed", "truncated"],
"tokens": ["sheet_header_height", "file_link", "card", "line", "soft"],
"platformNotes": "Placement is shared policy; rendering, file export, and platform-safe dismissal remain native adapters."
},
"miniapp_navigation": {
"purpose": "Opens locally bundled tools from primary sidebar navigation, without requiring account or device access.",
"anatomy": [
"outlined_apps_glyph",
"leading_aligned_label",
"full_row_touch_target"
],
"states": [
"idle",
"pressed",
"focused",
"signed_out",
"connected"
],
"tokens": [
"ink",
"body_large",
"control_touch_size"
],
"platformNotes": "Use a quiet 48vp icon-and-label row in compact and wide sidebars, with 16vp medium-weight navigation text, a 24vp icon slot and a 14vp gap. HarmonyOS uses the native outlined sys.symbol.square_grid_2x2 at 24vp, centered in its slot and tinted with the semantic ink color. Render the catalog inside the shell content pane so selecting it slides the right pane left as the drawer closes, using the existing 250ms ease-out shell motion. Sidebar session selection uses the same shell motion without a second route-push animation. Do not promote this navigation destination to a primary action on remote home."
},
"miniapp_catalog": {
"purpose": "Presents bundled apps with large visual previews that open each app directly.",
"anatomy": [
"back_control",
"centered_page_title",
"offline_caption",
"square_preview",
"app_title"
],
"states": [
"catalog",
"loading",
"error",
"app_open",
"light",
"dark"
],
"tokens": [
"page_bg",
"ink",
"muted",
"soft",
"body_large",
"title_small",
"body_medium",
"label_small"
],
"platformNotes": "Use a two-column library of square previews on compact screens and three columns when the gallery reaches 600vp. Clip previews to 24vp corners with 14vp column and 20vp row gaps; keep app names below images. The centered gallery caps at 1000vp, while open apps use full width. Reuse established showcase assets. Resize the grid without reloading an open app."
},
"welcome_home": {
"purpose": "Introduces the mobile companion and provides a direct connection action with an offline MiniApp alternative.",
"anatomy": [
"desktop_contour_brand_mark",
"centered_heading",
"connection_guidance",
"bottom_primary_action",
"quiet_miniapp_action"
],
"states": [
"disconnected",
"connecting",
"connected",
"light",
"dark",
"compact",
"wide"
],
"tokens": [
"page_bg",
"ink",
"muted",
"primary_action",
"content_on_action",
"body_medium"
],
"platformNotes": "Reuse the desktop contour mark at 104vp. Use a 22vp medium heading and 14vp supporting copy, a scrollable hero, 24vp side margins, and a 52vp capsule action constrained to 360vp. Keep connection state truthful; offline MiniApps remain available."
},
"startup_brand_reveal": {
"purpose": "Provides a short desktop-inspired identity transition on cold launch without waiting for network or account loading.",
"anatomy": [
"theme_background",
"desktop_contour_brand_mark"
],
"states": [
"cold_start",
"reduced_motion",
"backgrounded"
],
"tokens": [
"page_bg",
"ink"
],
"platformNotes": "Center the shared mark at 104vp above a 24vp medium OpenBitFun wordmark. Scale the mark from 0.94 to 1 over 360ms; reveal letters with opacity and an 8vp rise over 260ms each, starting at 140ms with 55ms staggering. Reserve the full wordmark width. Dissolve the overlay over 240ms starting at 1050ms. Remove on completion or background; never replay on foreground. Skip when the system reduced-motion setting is available and enabled. Clean up timers on removal."
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/apps/mobile/harmonyos/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@
*.pem
*.hap
*.app

# Generated from shared desktop MiniApp sources by entry/hvigorfile.ts
/entry/src/main/resources/rawfile/miniapps/
1 change: 1 addition & 0 deletions src/apps/mobile/harmonyos/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ through `LocaleController`. Do not import Web UI or mobile-web locale catalogs.
The current local HarmonyOS verification loop is:

```bash
node --test miniapps/*.test.cjs # bundled sources, browser bridge, and local storage compatibility
source scripts/ohos-env.sh
"$HVIGORW" --mode module -p product=default -p module=entry@default assembleHap --no-daemon
"$HVIGORW" --mode module -p module=entry@default -p ohos.test.type=LocalTest test --no-daemon
Expand Down
53 changes: 53 additions & 0 deletions src/apps/mobile/harmonyos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,56 @@ a local signing identity in DevEco Studio when installing the app on a device.

The current project targets HarmonyOS `6.1.1(24)` and supports
`6.0.1(21)` or newer on phone and tablet devices.

## Built-in MiniApps

Choose **MiniApps** from the sidebar to open Gomoku, Regex Playground,
or Daily Divination. The gallery uses two columns of rounded showcase previews
on phones and three on wide screens; tapping a preview opens the app directly.
Showcase images reuse the existing desktop artwork under
`src/web-ui/src/assets/miniapps/showcases/`. Navigation typography follows the
shared mobile body role, with the same proportions on compact and wide hosts.

These bundled pages run locally in ArkWeb without signing
in, pairing, a desktop connection, or internet access. Storage and clipboard
writes use the phone. Data is isolated by app and retained across app upgrades;
it is not synchronized with desktop MiniApps. Closing a page discards its
unsaved page state. Changing desktop targets has no effect on these local tools.

Every IDE/CLI HAP build runs `miniapps/generate.cjs` through
`entry/hvigorfile.ts`. It packages the existing shared MiniApp sources and
canonical appearance tokens into generated raw resources; do not edit those
outputs. `miniapps/mobile.css` owns phone-only layout adaptation. The pages
follow the app language and system light/dark appearance. They resize in place
for compact, wide, and folded/unfolded layouts.

The sandboxed iframe has no network access or local-file access. The native
bridge permits only per-app storage keys and clipboard writes; it has no generic
invoke, Worker, shell, workspace, AI, or Agent API. Local records are replaced
atomically, and malformed records are retained and return an error. These local
tools do not read any remote workspace or peer content, and do not execute jobs
on CLI/Detached Dispatch hosts. Existing Remote Connect protocols are unchanged.
Desktop catalogs, page transfer, and remote MiniApp execution are deferred.

Run the local test and HAP build commands in `AGENTS.md`, plus:

```bash
node --test miniapps/*.test.cjs
```

Before claiming device verification, exercise local/offline launches, saved data
after relaunch, clipboard, keyboard input, compact/wide layouts, light/dark
appearance, and a live resize/fold transition on supported hardware.

### Home and launch presentation

Cold launches reuse the desktop contour mark and reveal the OpenBitFun wordmark
letter by letter in approximately 1.3 seconds. This presentation does not wait
for network/account initialization, is dismissed when backgrounded, and does not
replay on foreground. The system reduced-motion preference skips it where the
platform exposes that preference (API 23+). Native launch artwork and the app
surface both follow the system light/dark theme.

The welcome surface keeps connection actions at the bottom and offers bundled
MiniApps without a desktop connection. Connected, connecting, and empty wide
surfaces retain their existing remote capability and session routing behavior.
3 changes: 3 additions & 0 deletions src/apps/mobile/harmonyos/entry/hvigorfile.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { hapTasks } from '@ohos/hvigor-ohos-plugin';

// Include the current shared sources on every IDE and CLI build.
require('../miniapps/generate.cjs').generate();

export default {
system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
Expand Down
17 changes: 17 additions & 0 deletions src/apps/mobile/harmonyos/entry/src/main/ets/i18n/EnUsMessages.ets
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
export const EN_US_MESSAGES: [string, string][] = [
['home.welcomeTitle', 'Pick up wherever you are'],
['home.welcomeDetail', 'Connect your computer to follow your tasks and continue the conversation.'],
['home.connectComputer', 'Connect computer'],
['home.connecting', 'Connecting…'],
['home.exploreMiniApps', 'Explore MiniApps'],
['home.miniAppsHint', 'Ready to use, even without a computer'],
['miniapp.allApps', 'All apps'],
['miniapp.availableOffline', 'Available offline'],

['miniapp.timeout', 'The local operation timed out. It may still finish; it was not retried.'],
['miniapp.title', 'MiniApps'],
['miniapp.refresh', 'Refresh'],
['miniapp.empty', 'No built-in MiniApps are available.'],
['miniapp.unsupported', 'This capability is unavailable in built-in mobile MiniApps.'],
['miniapp.invalidPage', 'The MiniApp page is incomplete. Reopen it.'],
['miniapp.closed', 'The MiniApp was closed or changed.'],
['miniapp.previewHint', 'Built in · Works offline. Data stays on this phone.'],
['notification.onboardingTitle', 'Task completion notifications'],
['notification.onboardingBody', 'Allow OpenBitFun to notify you when tasks finish. You can change this later in system settings.'],
['notification.onboardingLater', 'Later'],
Expand Down
17 changes: 17 additions & 0 deletions src/apps/mobile/harmonyos/entry/src/main/ets/i18n/ZhCnMessages.ets
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
export const ZH_CN_MESSAGES: [string, string][] = [
['home.welcomeTitle', '让灵感,随时继续'],
['home.welcomeDetail', '连接你的电脑,查看任务进度,继续对话。'],
['home.connectComputer', '连接电脑'],
['home.connecting', '正在连接…'],
['home.exploreMiniApps', '探索小应用'],
['home.miniAppsHint', '无需连接电脑,也能即刻使用'],
['miniapp.allApps', '全部应用'],
['miniapp.availableOffline', '离线可用'],

['miniapp.timeout', '本地操作超时,操作可能仍会完成,未自动重试。'],
['miniapp.title', '小应用'],
['miniapp.refresh', '刷新'],
['miniapp.empty', '暂无内置小应用。'],
['miniapp.unsupported', '手机内置小应用不支持此能力。'],
['miniapp.invalidPage', '小应用页面数据不完整,请重新打开。'],
['miniapp.closed', '小应用已关闭或切换。'],
['miniapp.previewHint', '内置应用 · 离线可用,数据保存在本机。'],
['notification.onboardingTitle', '开启任务完成提醒'],
['notification.onboardingBody', '允许 OpenBitFun 在任务完成时发送通知。你可以稍后在系统设置中更改。'],
['notification.onboardingLater', '稍后'],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
export interface MiniAppSummary {
id: string;
name: string;
description: string;
}

export interface MiniAppBridgeParams {
key?: string;
value?: Object | null;
text?: string;
}

export interface MiniAppBridgeRequest {
id: string;
method: string;
params?: MiniAppBridgeParams;
}

export interface MiniAppBridgeError { message: string; }
export interface MiniAppBridgeReply {
jsonrpc: string;
id: string;
result?: Object | null;
error?: MiniAppBridgeError;
}

/** Local data source, deliberately independent of any connection/session. */
export interface MiniAppHost {
list(locale: string): Promise<MiniAppSummary[]>;
document(appId: string): Promise<string>;
call(appId: string, method: string, params: MiniAppBridgeParams): Promise<Object | null>;
}

export interface MiniAppActions {
open: () => void;
close: () => void;
back: () => void;
refresh: () => void;
select: (id: string) => void;
request: (raw: string, revision: number) => Promise<string>;
}

export function emptyMiniAppActions(): MiniAppActions {
return { open: () => {}, close: () => {}, back: () => {}, refresh: () => {}, select: () => {},
request: async (_raw: string, _revision: number): Promise<string> => '' };
}
Loading