commit 1a55ace5c450be5b2bc9e3b5c9fdeac84dd7d3c7
parent 2cc1c29d2c931c37cede4148cc34a1fa9d8eaf40
Author: triesap <triesap@radroots.dev>
Date: Fri, 26 Dec 2025 19:50:15 +0000
ble: add meshnet packet helpers
- Export meshnet module from public index
- Define meshnet profile and packet type unions
- Add runtime validators for parsed packet payloads
- Provide packet encode/decode helpers for BleMessage text
Diffstat:
3 files changed, 125 insertions(+), 20 deletions(-)
diff --git a/apps-lib/src/lib/components/view-stack.svelte b/apps-lib/src/lib/components/view-stack.svelte
@@ -54,6 +54,21 @@
} = $props();
const view_context = writable<ViewContext<string>>({
+ active_view: "",
+ mode: "stack",
+ fade: true,
+ transition_ms: DEFAULT_TRANSITION_MS,
+ opacity_inactive: DEFAULT_OPACITY_INACTIVE,
+ scale_inactive: DEFAULT_SCALE_INACTIVE,
+ offset_x: DEFAULT_OFFSET_X,
+ offset_y: DEFAULT_OFFSET_Y,
+ blur_inactive_px: DEFAULT_BLUR_INACTIVE_PX,
+ pointer_events_inactive: DEFAULT_POINTER_EVENTS_INACTIVE,
+ z_index_active: DEFAULT_Z_INDEX_ACTIVE,
+ z_index_inactive: DEFAULT_Z_INDEX_INACTIVE,
+ });
+
+ const view_context_value = $derived((): ViewContext<string> => ({
active_view: basis.active_view,
mode: basis.mode ?? "stack",
fade: basis.fade ?? true,
@@ -67,30 +82,12 @@
basis.pointer_events_inactive ?? DEFAULT_POINTER_EVENTS_INACTIVE,
z_index_active: basis.z_index_active ?? DEFAULT_Z_INDEX_ACTIVE,
z_index_inactive: basis.z_index_inactive ?? DEFAULT_Z_INDEX_INACTIVE,
- });
+ }));
set_context(VIEW_CONTEXT_KEY, view_context);
$effect(() => {
- view_context.set({
- active_view: basis.active_view,
- mode: basis.mode ?? "stack",
- fade: basis.fade ?? true,
- transition_ms: basis.transition_ms ?? DEFAULT_TRANSITION_MS,
- opacity_inactive:
- basis.opacity_inactive ?? DEFAULT_OPACITY_INACTIVE,
- scale_inactive: basis.scale_inactive ?? DEFAULT_SCALE_INACTIVE,
- offset_x: basis.offset_x ?? DEFAULT_OFFSET_X,
- offset_y: basis.offset_y ?? DEFAULT_OFFSET_Y,
- blur_inactive_px:
- basis.blur_inactive_px ?? DEFAULT_BLUR_INACTIVE_PX,
- pointer_events_inactive:
- basis.pointer_events_inactive ??
- DEFAULT_POINTER_EVENTS_INACTIVE,
- z_index_active: basis.z_index_active ?? DEFAULT_Z_INDEX_ACTIVE,
- z_index_inactive:
- basis.z_index_inactive ?? DEFAULT_Z_INDEX_INACTIVE,
- });
+ view_context.set(view_context_value);
});
const mode = $derived(basis.mode ?? "stack");
diff --git a/ble/src/index.ts b/ble/src/index.ts
@@ -1,4 +1,5 @@
export * from "./error.js";
+export * from "./meshnet.js";
export * from "./messages.js";
export * from "./types.js";
export * from "./web.js";
diff --git a/ble/src/meshnet.ts b/ble/src/meshnet.ts
@@ -0,0 +1,107 @@
+import { ble_message_text } from "./messages.js";
+import type { BleMessage } from "./types.js";
+
+export type BleMeshnetProfile = {
+ display_name: string;
+ device_label: string;
+};
+
+export type BleMeshnetPacketProfile = {
+ type: "meshnet.profile.v1";
+ profile: BleMeshnetProfile;
+ timestamp_ms: number;
+};
+
+export type BleMeshnetPacketChat = {
+ type: "meshnet.chat.v1";
+ message_id: string;
+ profile: BleMeshnetProfile;
+ text: string;
+ timestamp_ms: number;
+};
+
+export type BleMeshnetPacket = BleMeshnetPacketProfile | BleMeshnetPacketChat;
+
+const is_record = (value: unknown): value is Record<string, unknown> =>
+ typeof value === "object" && value !== null && !Array.isArray(value);
+
+const is_string = (value: unknown): value is string => typeof value === "string";
+
+const is_number = (value: unknown): value is number =>
+ typeof value === "number" && Number.isFinite(value);
+
+const is_profile = (value: unknown): value is BleMeshnetProfile => {
+ if (!is_record(value)) return false;
+ return is_string(value.display_name) && is_string(value.device_label);
+};
+
+const is_packet_profile = (value: unknown): value is BleMeshnetPacketProfile => {
+ if (!is_record(value)) return false;
+ if (value.type !== "meshnet.profile.v1") return false;
+ if (!is_profile(value.profile)) return false;
+ if (!is_number(value.timestamp_ms)) return false;
+ return true;
+};
+
+const is_packet_chat = (value: unknown): value is BleMeshnetPacketChat => {
+ if (!is_record(value)) return false;
+ if (value.type !== "meshnet.chat.v1") return false;
+ if (!is_string(value.message_id)) return false;
+ if (!is_profile(value.profile)) return false;
+ if (!is_string(value.text)) return false;
+ if (!is_number(value.timestamp_ms)) return false;
+ return true;
+};
+
+export const ble_meshnet_packet_profile = (
+ profile: BleMeshnetProfile,
+ timestamp_ms: number,
+): BleMeshnetPacketProfile => {
+ return {
+ type: "meshnet.profile.v1",
+ profile,
+ timestamp_ms,
+ };
+};
+
+export const ble_meshnet_packet_chat = (
+ profile: BleMeshnetProfile,
+ text: string,
+ message_id: string,
+ timestamp_ms: number,
+): BleMeshnetPacketChat => {
+ return {
+ type: "meshnet.chat.v1",
+ message_id,
+ profile,
+ text,
+ timestamp_ms,
+ };
+};
+
+export const ble_meshnet_message_from_packet = (
+ packet: BleMeshnetPacket,
+): string => {
+ return JSON.stringify(packet);
+};
+
+export const ble_meshnet_packet_from_text = (
+ text: string,
+): BleMeshnetPacket | undefined => {
+ let parsed: unknown;
+ try {
+ parsed = JSON.parse(text);
+ } catch {
+ return;
+ }
+ if (is_packet_profile(parsed)) return parsed;
+ if (is_packet_chat(parsed)) return parsed;
+};
+
+export const ble_meshnet_packet_from_message = (
+ message: BleMessage,
+): BleMeshnetPacket | undefined => {
+ const text = ble_message_text(message.bytes);
+ if (!text) return;
+ return ble_meshnet_packet_from_text(text);
+};