@tak-ps/cloudtak 13.78.1 → 13.79.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -195,16 +195,17 @@ export declare class PluginAPI {
195
195
  has: (uid: string) => boolean;
196
196
  };
197
197
  /**
198
- * Manage the Map Status Bar
198
+ * Manage the floating plugin pane at the bottom centre of the map.
199
+ * The pane is only rendered while at least one item is registered.
199
200
  */
200
201
  get bottomBar(): {
201
202
  /**
202
- * Add a component to the centre of the bottom status bar
203
+ * Add a component to the floating plugin pane
203
204
  * @param item The bottom bar item configuration
204
205
  */
205
206
  add: (item: BottomBarItemConfig) => void;
206
207
  /**
207
- * Remove a previously registered bottom bar component by key
208
+ * Remove a previously registered plugin pane component by key
208
209
  * @param key The key of the item to remove
209
210
  */
210
211
  remove: (key: string) => void;
@@ -0,0 +1,24 @@
1
+ import { type Observable } from 'dexie';
2
+ import { type DBProfileFile } from '../database.ts';
3
+ import BaseInterface from './interface.ts';
4
+ import type { BaseInterface_ListOptions } from './interface.ts';
5
+ export type ProfileFile_ListOptions = BaseInterface_ListOptions & {
6
+ filter?: string;
7
+ };
8
+ /**
9
+ * Metadata for profile assets whose PMTiles archive has been downloaded to
10
+ * this device, so the Files menu can list them without the server
11
+ */
12
+ export default class ProfileFileManager extends BaseInterface {
13
+ static readonly listCacheKey = "profile_file";
14
+ static count(): Promise<number>;
15
+ static liveCount(): Observable<number>;
16
+ static list(opts?: ProfileFile_ListOptions): Promise<DBProfileFile[]>;
17
+ static liveList(opts?: ProfileFile_ListOptions): Observable<DBProfileFile[]>;
18
+ static from(id: string): Promise<DBProfileFile | undefined>;
19
+ static liveFrom(id: string): Observable<DBProfileFile | undefined>;
20
+ static generate(file: DBProfileFile): Promise<DBProfileFile>;
21
+ static bulkPut(files: DBProfileFile[]): Promise<void>;
22
+ static update(id: string, file: Partial<DBProfileFile>): Promise<void>;
23
+ static delete(id: string): Promise<void>;
24
+ }
@@ -0,0 +1,39 @@
1
+ type __VLS_Props = {
2
+ overlayUrls: Set<string>;
3
+ isOffline: boolean;
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
+ "create-overlay": (asset: {
7
+ id: string;
8
+ created: string;
9
+ updated: string;
10
+ username: string;
11
+ path: string;
12
+ name: string;
13
+ iconset: null | string;
14
+ size: number;
15
+ channels: number[];
16
+ artifacts: {
17
+ ext: string;
18
+ size: number;
19
+ }[];
20
+ }) => any;
21
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
22
+ "onCreate-overlay"?: ((asset: {
23
+ id: string;
24
+ created: string;
25
+ updated: string;
26
+ username: string;
27
+ path: string;
28
+ name: string;
29
+ iconset: null | string;
30
+ size: number;
31
+ channels: number[];
32
+ artifacts: {
33
+ ext: string;
34
+ size: number;
35
+ }[];
36
+ }) => any) | undefined;
37
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
38
+ declare const _default: typeof __VLS_export;
39
+ export default _default;
@@ -3,6 +3,7 @@ type __VLS_Props = {
3
3
  asset: ProfileFile;
4
4
  currentUsername: string;
5
5
  overlayUrls: Set<string>;
6
+ offlineIds: Set<string>;
6
7
  rename?: {
7
8
  id: string;
8
9
  loading: boolean;
@@ -10,6 +11,21 @@ type __VLS_Props = {
10
11
  };
11
12
  };
12
13
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
14
+ offline: (asset: {
15
+ id: string;
16
+ created: string;
17
+ updated: string;
18
+ username: string;
19
+ path: string;
20
+ name: string;
21
+ iconset: null | string;
22
+ size: number;
23
+ channels: number[];
24
+ artifacts: {
25
+ ext: string;
26
+ size: number;
27
+ }[];
28
+ }) => any;
13
29
  download: (asset: {
14
30
  id: string;
15
31
  created: string;
@@ -24,7 +40,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
24
40
  ext: string;
25
41
  size: number;
26
42
  }[];
27
- }, type: "original" | "pmtiles") => any;
43
+ }, type: "pmtiles" | "original") => any;
28
44
  delete: (asset: {
29
45
  id: string;
30
46
  created: string;
@@ -134,6 +150,21 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
134
150
  "rename-submit": () => any;
135
151
  "rename-cancel": () => any;
136
152
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
153
+ onOffline?: ((asset: {
154
+ id: string;
155
+ created: string;
156
+ updated: string;
157
+ username: string;
158
+ path: string;
159
+ name: string;
160
+ iconset: null | string;
161
+ size: number;
162
+ channels: number[];
163
+ artifacts: {
164
+ ext: string;
165
+ size: number;
166
+ }[];
167
+ }) => any) | undefined;
137
168
  onDownload?: ((asset: {
138
169
  id: string;
139
170
  created: string;
@@ -148,7 +179,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
148
179
  ext: string;
149
180
  size: number;
150
181
  }[];
151
- }, type: "original" | "pmtiles") => any) | undefined;
182
+ }, type: "pmtiles" | "original") => any) | undefined;
152
183
  onDelete?: ((asset: {
153
184
  id: string;
154
185
  created: string;
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -0,0 +1,39 @@
1
+ import type { ProfileFile } from '../../../types.ts';
2
+ type __VLS_Props = {
3
+ asset: ProfileFile;
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
+ done: (asset: {
7
+ id: string;
8
+ created: string;
9
+ updated: string;
10
+ username: string;
11
+ path: string;
12
+ name: string;
13
+ iconset: null | string;
14
+ size: number;
15
+ channels: number[];
16
+ artifacts: {
17
+ ext: string;
18
+ size: number;
19
+ }[];
20
+ }) => any;
21
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
22
+ onDone?: ((asset: {
23
+ id: string;
24
+ created: string;
25
+ updated: string;
26
+ username: string;
27
+ path: string;
28
+ name: string;
29
+ iconset: null | string;
30
+ size: number;
31
+ channels: number[];
32
+ artifacts: {
33
+ ext: string;
34
+ size: number;
35
+ }[];
36
+ }) => any) | undefined;
37
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
38
+ declare const _default: typeof __VLS_export;
39
+ export default _default;
@@ -1,5 +1,5 @@
1
1
  import Dexie, { type EntityTable } from 'dexie';
2
- import type { Feature, GroupChannel, Mission, MissionRole, MissionChange, MissionLayer, MissionLog, Contact, Server, ProfileOverlayList } from './types.ts';
2
+ import type { Feature, GroupChannel, Mission, MissionRole, MissionChange, MissionLayer, MissionLog, Contact, Server, ProfileFile, ProfileOverlayList } from './types.ts';
3
3
  export interface DBIcon {
4
4
  /** Maplibre image id, e.g. "<iconsetUid>:<icon-path-without-extension>" */
5
5
  name: string;
@@ -74,6 +74,8 @@ export interface DBIconset {
74
74
  skip_resize: boolean;
75
75
  }
76
76
  export type DBOverlay = ProfileOverlayList["items"][number];
77
+ /** Profile asset metadata retained for files downloaded for offline use */
78
+ export type DBProfileFile = ProfileFile;
77
79
  export interface DBFilter {
78
80
  id: string;
79
81
  external: string;
@@ -233,6 +235,7 @@ export type DatabaseType = Dexie & {
233
235
  mission_template_log: EntityTable<DBMissionTemplateLog, 'id'>;
234
236
  kv: EntityTable<DBKV, 'key'>;
235
237
  profile: EntityTable<DBProfileConfig, 'key'>;
238
+ profile_file: EntityTable<DBProfileFile, 'id'>;
236
239
  config: EntityTable<DBConfig, 'key'>;
237
240
  cache: EntityTable<DBCache, 'key'>;
238
241
  contact: EntityTable<Contact, 'uid'>;
@@ -1,6 +1,7 @@
1
1
  import type { Map as MapLibreMap, IControl, ControlPosition } from 'maplibre-gl';
2
2
  import type { Feature, LineString, Position } from 'geojson';
3
3
  export type NavigationDirection = 'forward' | 'reverse';
4
+ export type NavigationMode = 'route' | 'point';
4
5
  export interface NavigationState {
5
6
  /** Distance remaining along the route to the destination, in kilometers. */
6
7
  remaining: number;
@@ -14,6 +15,8 @@ export interface NavigationState {
14
15
  destination: Position;
15
16
  /** Which end of the route is the destination. */
16
17
  direction: NavigationDirection;
18
+ /** Whether navigating along a Route or straight-line to a Point. */
19
+ mode: NavigationMode;
17
20
  }
18
21
  export type RoutingControlOptions = {
19
22
  onUpdate?: (state: NavigationState | null) => void;
@@ -23,6 +26,7 @@ export declare class RoutingControl implements IControl {
23
26
  private map?;
24
27
  private container?;
25
28
  private route;
29
+ private destination;
26
30
  private location;
27
31
  private direction;
28
32
  private state;
@@ -35,7 +39,14 @@ export declare class RoutingControl implements IControl {
35
39
  onRemove(): void;
36
40
  getDefaultPosition(): ControlPosition;
37
41
  get active(): boolean;
42
+ getMode(): NavigationMode | null;
43
+ clear(): void;
38
44
  setRoute(route: Feature<LineString> | null): void;
45
+ /**
46
+ * Navigate straight-line to a single Point - the remaining line is redrawn
47
+ * from the user's current location to the destination on every update.
48
+ */
49
+ setDestination(destination: Position | null): void;
39
50
  /**
40
51
  * Rehydrate a previously persisted route from the KV store. Returns true
41
52
  * if a route was restored - the caller is responsible for re-feeding
@@ -49,6 +60,7 @@ export declare class RoutingControl implements IControl {
49
60
  setDirection(direction: NavigationDirection): void;
50
61
  reverse(): void;
51
62
  getDirection(): NavigationDirection;
63
+ getDestination(): Position | null;
52
64
  getState(): NavigationState | null;
53
65
  private recompute;
54
66
  private buildCollection;
@@ -5,7 +5,7 @@ import BottomBarManager from './modules/bottombar.ts';
5
5
  import * as Comlink from 'comlink';
6
6
  import COT from '../base/cot.ts';
7
7
  import RoutingControl from '../lib/routing/main.ts';
8
- import type { NavigationState, NavigationDirection } from '../lib/routing/main.ts';
8
+ import type { NavigationState, NavigationDirection, NavigationMode } from '../lib/routing/main.ts';
9
9
  import { LocationState } from '../utils/events.ts';
10
10
  import Overlay from '../base/overlay-class.ts';
11
11
  import Subscription from '../base/subscription.ts';
@@ -13,6 +13,7 @@ import * as mapgl from 'maplibre-gl';
13
13
  import type Atlas from '../workers/atlas.ts';
14
14
  import type { Feature } from '../types.ts';
15
15
  import type { LngLat, Point, MapGeoJSONFeature } from 'maplibre-gl';
16
+ import type { Position as GeoJSONPosition } from 'geojson';
16
17
  export type TAKNotification = {
17
18
  type: string;
18
19
  name: string;
@@ -48,14 +49,21 @@ export declare const useMapStore: import("pinia").StoreDefinition<"cloudtak", {
48
49
  lng: number;
49
50
  } | null;
50
51
  gpsSpeed: number | null;
52
+ gpsAltitude: number | null;
53
+ gpsHeading: number | null;
54
+ deviceHeading: number | null;
51
55
  navigation: {
52
56
  active: boolean;
53
57
  cotId: string | null;
54
58
  callsign: string | null;
55
59
  direction: NavigationDirection;
60
+ mode: NavigationMode;
61
+ destination: GeoJSONPosition | null;
56
62
  state: NavigationState | null;
57
63
  };
58
64
  distanceUnit: string;
65
+ elevationUnit: string;
66
+ speedUnit: string;
59
67
  coordFormat: string;
60
68
  defaultPointType: string;
61
69
  manualLocationMode: boolean;
@@ -74,6 +82,7 @@ export declare const useMapStore: import("pinia").StoreDefinition<"cloudtak", {
74
82
  container?: HTMLElement;
75
83
  hasSnapping: boolean;
76
84
  hasNoChannels: boolean;
85
+ offlineTiles: Set<string>;
77
86
  channelChange: boolean;
78
87
  isMapLoaded: boolean;
79
88
  isMapLoadedFully: boolean;
@@ -111,6 +120,8 @@ export declare const useMapStore: import("pinia").StoreDefinition<"cloudtak", {
111
120
  routingControl: () => RoutingControl | undefined;
112
121
  syncRoutingControl: () => void;
113
122
  startNavigation: (cotId: string) => Promise<void>;
123
+ navigateTo: (destination: GeoJSONPosition, callsign?: string) => void;
124
+ commitNavigation: (control: RoutingControl, cotId: string | null, callsign?: string) => void;
114
125
  restoreNavigation: () => Promise<void>;
115
126
  stopNavigation: () => void;
116
127
  reverseNavigation: () => void;
@@ -2,21 +2,21 @@ import type { Component, Ref } from 'vue';
2
2
  export type BottomBarItemConfig = {
3
3
  /** Unique identifier for the item */
4
4
  key: string;
5
- /** Vue component rendered in the centre of the bottom status bar */
5
+ /** Vue component rendered in the floating plugin pane at the bottom centre of the map */
6
6
  component: Component;
7
7
  };
8
8
  /**
9
- * Manages plugin-registered components displayed in the centre of the map status bar
9
+ * Manages plugin-registered components displayed in the floating plugin pane
10
10
  */
11
11
  export default class BottomBarManager {
12
12
  pluginItems: Ref<BottomBarItemConfig[]>;
13
13
  constructor();
14
14
  /**
15
- * Add a component to the centre of the bottom status bar
15
+ * Add a component to the floating plugin pane
16
16
  */
17
17
  addItem(item: BottomBarItemConfig): void;
18
18
  /**
19
- * Remove a previously registered bottom bar component by key
19
+ * Remove a previously registered plugin pane component by key
20
20
  */
21
21
  removeItem(key: string): void;
22
22
  }
@@ -0,0 +1,6 @@
1
+ import type { OverlayTileJSON } from '../../types.ts';
2
+ export { offlineTileTemplate, isOfflineTileJSON } from '../../utils/offline-tiles.ts';
3
+ export declare function invalidateOfflinePMTiles(assetId: string): void;
4
+ export declare function offlineTileJSON(assetId: string): Promise<OverlayTileJSON>;
5
+ export declare function setNetworkTileTemplate(assetId: string, template: string): void;
6
+ export declare function registerPMTilesProtocol(): void;
@@ -7,6 +7,8 @@ export declare enum WorkerMessageType {
7
7
  Profile_Display_Zoom = "cloudtak:profile:display:zoom",
8
8
  Profile_Icon_Rotation = "cloudtak:profile:icon:rotation",
9
9
  Profile_Distance_Unit = "cloudtak:profile:distance:unit",
10
+ Profile_Elevation_Unit = "cloudtak:profile:elevation:unit",
11
+ Profile_Speed_Unit = "cloudtak:profile:speed:unit",
10
12
  Profile_Location_Coordinates = "cloudtak:profile:location:coordinates",
11
13
  Profile_Location_Source = "cloudtak:profile:location:source",
12
14
  Feature_Archived_Added = "cloudtak:feature:archived:added",
@@ -24,6 +26,8 @@ export declare enum WorkerMessageType {
24
26
  Sync_Start = "cloudtak:sync:start",
25
27
  Sync_Complete = "cloudtak:sync:complete",
26
28
  Iconset_Change = "cloudtak:iconset:change",
29
+ Tiles_Downloaded = "cloudtak:tiles:downloaded",
30
+ Tiles_Removed = "cloudtak:tiles:removed",
27
31
  VideoWall_Ping = "cloudtak:videowall:ping",
28
32
  VideoWall_Pong = "cloudtak:videowall:pong",
29
33
  VideoWall_Refresh = "cloudtak:videowall:refresh"
@@ -0,0 +1,23 @@
1
+ import { type Header } from 'pmtiles';
2
+ import type { OverlayTileJSON } from '../types.ts';
3
+ export declare const OFFLINE_TILES_DIR = "pmtiles";
4
+ export declare const PMTILES_PROTOCOL = "cloudtak-pmtiles";
5
+ export declare function offlineTilesFileName(assetId: string): string;
6
+ export declare function offlineTilesSizeKey(assetId: string): string;
7
+ export declare function profileAssetIdFromUrl(url: string | null | undefined): string | undefined;
8
+ export declare function offlineTilesDirectory(): Promise<FileSystemDirectoryHandle>;
9
+ export declare function offlineTilesHandle(assetId: string): Promise<FileSystemFileHandle | undefined>;
10
+ /**
11
+ * A file only counts once its byte size matches the size recorded when the
12
+ * download completed - a crash mid-stream leaves a truncated archive behind.
13
+ */
14
+ export declare function hasCompleteOfflineTiles(assetId: string): Promise<boolean>;
15
+ export declare function offlineTileTemplate(assetId: string): string;
16
+ export declare function isOfflineTileJSON(tilejson: {
17
+ tiles: string[];
18
+ }): boolean;
19
+ /**
20
+ * Mirror of the hosted tile server's TileJSON, built from the archive header
21
+ * and metadata so an overlay can be created without ever having been online.
22
+ */
23
+ export declare function offlineTileJSON(assetId: string, header: Header, metadata: unknown): OverlayTileJSON;
@@ -0,0 +1,11 @@
1
+ import type Atlas from './atlas.ts';
2
+ import type { DBProfileFile } from '../database.ts';
3
+ export type TilesProgress = (percent: number) => void;
4
+ export default class AtlasTiles {
5
+ atlas: Atlas;
6
+ constructor(atlas: Atlas);
7
+ has(assetId: string): Promise<boolean>;
8
+ list(): Promise<string[]>;
9
+ remove(assetId: string): Promise<void>;
10
+ download(asset: DBProfileFile, onProgress?: TilesProgress): Promise<void>;
11
+ }
@@ -4,6 +4,7 @@ import AtlasProfile from './atlas-profile.ts';
4
4
  import AtlasDatabase from './atlas-database.ts';
5
5
  import AtlasConnection from './atlas-connection.ts';
6
6
  import AtlasSync from './atlas-sync.ts';
7
+ import AtlasTiles from './atlas-tiles.ts';
7
8
  export default class Atlas {
8
9
  channel: BroadcastChannel;
9
10
  token: string;
@@ -13,6 +14,7 @@ export default class Atlas {
13
14
  conn: AtlasConnection & Comlink.ProxyMarked;
14
15
  profile: AtlasProfile & Comlink.ProxyMarked;
15
16
  sync: AtlasSync & Comlink.ProxyMarked;
17
+ tiles: AtlasTiles & Comlink.ProxyMarked;
16
18
  constructor();
17
19
  postMessage(msg: WorkerMessage): Promise<void>;
18
20
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tak-ps/cloudtak",
3
3
  "type": "module",
4
- "version": "13.78.1",
4
+ "version": "13.79.0",
5
5
  "types": "dist/types/plugin.d.ts",
6
6
  "files": [
7
7
  "dist/types"
@@ -93,6 +93,7 @@
93
93
  "openapi-fetch": "^0.17.0",
94
94
  "phone": "^3.1.59",
95
95
  "pinia": "^4.0.0",
96
+ "pmtiles": "^4.5.0",
96
97
  "rxjs": "^7.8.2",
97
98
  "sortablejs": "^1.15.2",
98
99
  "terra-draw": "^1.22.0",
@@ -114,8 +115,8 @@
114
115
  "@types/mapbox__tile-cover": "^3.0.4",
115
116
  "@types/sortablejs": "^1.15.8",
116
117
  "@vitejs/plugin-vue": "^6.0.0",
117
- "@vitest/coverage-v8": "^4.1.0",
118
- "@vitest/ui": "^4.1.0",
118
+ "@vitest/coverage-v8": "^5.0.0",
119
+ "@vitest/ui": "^5.0.0",
119
120
  "@vue/devtools-api": "^8.1.5",
120
121
  "@vue/test-utils": "^2.4.6",
121
122
  "@vueuse/rxjs": "^14.1.0",
@@ -129,7 +130,7 @@
129
130
  "typescript": "^6.0.0",
130
131
  "typescript-eslint": "^8.20.0",
131
132
  "vite": "^8.0.0",
132
- "vitest": "^4.1.0",
133
+ "vitest": "^5.0.0",
133
134
  "vue-tsc": "^3.0.0-alpha.10"
134
135
  },
135
136
  "browserslist": [
@@ -1,12 +0,0 @@
1
- type __VLS_Props = {
2
- mode: string;
3
- };
4
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
- "set-location": (...args: any[]) => void;
6
- "to-location": (...args: any[]) => void;
7
- }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
8
- "onSet-location"?: ((...args: any[]) => any) | undefined;
9
- "onTo-location"?: ((...args: any[]) => any) | undefined;
10
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
11
- declare const _default: typeof __VLS_export;
12
- export default _default;