@octane-xplat/platform 0.1.0 → 0.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octane-xplat/platform",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts",
@@ -16,12 +16,23 @@
16
16
  "nativescript-clipboard": "2.1.1"
17
17
  },
18
18
  "devDependencies": {
19
+ "@nativescript-community/octane": "0.2.1",
19
20
  "@nativescript/core": "9.1.2",
20
21
  "octane": "0.4.0"
21
22
  },
22
23
  "peerDependencies": {
24
+ "@nativescript-community/octane": ">=0.2.1 <1",
25
+ "@nativescript/core": ">=9.1.0 <10",
23
26
  "octane": ">=0.1.51 <1"
24
27
  },
28
+ "peerDependenciesMeta": {
29
+ "@nativescript-community/octane": {
30
+ "optional": true
31
+ },
32
+ "@nativescript/core": {
33
+ "optional": true
34
+ }
35
+ },
25
36
  "description": "Headless platform capabilities for Octane xplat — web + NativeScript leaf pairs",
26
37
  "files": [
27
38
  "src"
@@ -1,17 +1,30 @@
1
- // Clipboard — pasteboard via nativescript-clipboard (sync API wrapped async
2
- // to keep the shared contract awaitable on both sides).
3
- import { getTextSync, setTextSync } from 'nativescript-clipboard';
1
+ // Core provides native clipboard writes. The plugin remains for reading text,
2
+ // which Core does not expose.
3
+ import { Utils } from '@nativescript/core';
4
+ import { getTextSync } from 'nativescript-clipboard';
4
5
 
5
- export const clipboard = {
6
- async write(text: string): Promise<boolean> {
7
- setTextSync(text);
6
+ async function writeText(text: string): Promise<boolean> {
7
+ try {
8
+ Utils.copyToClipboard(text);
8
9
  return true;
9
- },
10
- async read(): Promise<string | null> {
11
- try {
12
- return getTextSync();
13
- } catch {
14
- return null;
15
- }
16
- },
10
+ } catch {
11
+ return false;
12
+ }
13
+ }
14
+
15
+ async function readText(): Promise<string | null> {
16
+ try {
17
+ return getTextSync();
18
+ } catch {
19
+ return null;
20
+ }
21
+ }
22
+
23
+ export const clipboard = {
24
+ canCopy: true,
25
+ writeText,
26
+ readText,
27
+ // Keep the original names available for existing consumers.
28
+ write: writeText,
29
+ read: readText,
17
30
  };
@@ -1,18 +1,29 @@
1
- // Clipboard — async permission-gated on web; read may be denied silently.
1
+ // Clipboard — browser support is feature-detected; writes can still be denied
2
+ // by browser policy or permission state.
3
+ const canCopy = typeof navigator !== 'undefined' && typeof navigator.clipboard?.writeText === 'function';
4
+
5
+ async function writeText(text: string): Promise<boolean> {
6
+ try {
7
+ await navigator.clipboard.writeText(text);
8
+ return true;
9
+ } catch {
10
+ return false;
11
+ }
12
+ }
13
+
14
+ async function readText(): Promise<string | null> {
15
+ try {
16
+ return await navigator.clipboard.readText();
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+
2
22
  export const clipboard = {
3
- async write(text: string): Promise<boolean> {
4
- try {
5
- await navigator.clipboard.writeText(text);
6
- return true;
7
- } catch {
8
- return false;
9
- }
10
- },
11
- async read(): Promise<string | null> {
12
- try {
13
- return await navigator.clipboard.readText();
14
- } catch {
15
- return null;
16
- }
17
- },
23
+ canCopy,
24
+ writeText,
25
+ readText,
26
+ // Keep the original names available for existing consumers.
27
+ write: writeText,
28
+ read: readText,
18
29
  };
@@ -20,5 +20,11 @@ export const files = {
20
20
  f.writeTextSync(text);
21
21
  return { name, uri: p };
22
22
  },
23
- release(_ref: FileRef): void {},
23
+ release(ref: FileRef): void {
24
+ const cachePrefix = knownFolders.temp().path + '/';
25
+ if (!ref.uri.startsWith(cachePrefix)) return;
26
+ try {
27
+ File.fromPath(ref.uri).removeSync();
28
+ } catch {}
29
+ },
24
30
  };
package/src/index.ts CHANGED
@@ -3,9 +3,20 @@
3
3
  // Hook-bearing services live in .tsrx files with .ts shims so tsc's
4
4
  // moduleSuffixes can reach them (docs/module-resolution.md).
5
5
  export type {
6
- AppState, BiometricsImpl, Capability, DeviceInfo, FileRef, HapticsImpl,
7
- Insets, Locale, NotificationsImpl, PermissionKind, SecureStore,
8
- ShareResult, WindowSize,
6
+ AppState,
7
+ BiometricsImpl,
8
+ Capability,
9
+ DeviceInfo,
10
+ FileRef,
11
+ HapticsImpl,
12
+ PickedImage,
13
+ Insets,
14
+ Locale,
15
+ NotificationsImpl,
16
+ PermissionKind,
17
+ SecureStore,
18
+ ShareResult,
19
+ WindowSize,
9
20
  } from './types';
10
21
  export { device } from './device';
11
22
  export { storage } from './storage';
@@ -1,15 +1,37 @@
1
- // Media picking — @nativescript/imagepicker presents the system picker and
2
- // yields ImageAssets; the FileRef uri is the asset's file path.
3
- import { create as createImagePicker } from '@nativescript/imagepicker';
4
- import type { FileRef } from './types';
1
+ // Media picking — store a temporary JPEG for NativeScript Image and return a
2
+ // data URL for APIs that persist image payloads.
3
+ import { create as createImagePicker, ImagePickerMediaType } from '@nativescript/imagepicker';
4
+ import { ImageSource, knownFolders, path } from '@nativescript/core';
5
+ import { files } from './files';
6
+ import type { PickedImage } from './types';
5
7
 
6
8
  export const media = {
7
- async pickImage(): Promise<FileRef | null> {
8
- const picker = createImagePicker({ mode: 'single' });
9
- await picker.authorize();
10
- const [asset] = await picker.present();
11
- if (!asset) return null;
12
- const uri = (asset as any).ios ?? (asset as any).android ?? '';
13
- return { name: 'image', uri: String(uri) };
9
+ /** Opens the native image picker; returns null when permission is denied or selection is canceled. */
10
+ async pickImage(): Promise<PickedImage | null> {
11
+ const picker = createImagePicker({ mode: 'single', mediaType: ImagePickerMediaType.Image });
12
+ const permission = await picker.authorize();
13
+ if (!permission.authorized) return null;
14
+
15
+ const [selection] = await picker.present();
16
+ if (!selection) return null;
17
+
18
+ const source = await ImageSource.fromAsset(selection.asset);
19
+ const filename = `octane-image-${Date.now()}-${Math.random().toString(36).slice(2)}.jpg`;
20
+ const uri = path.join(knownFolders.temp().path, filename);
21
+ const ref = {
22
+ name: selection.originalFilename || selection.filename || 'image.jpg',
23
+ uri,
24
+ };
25
+
26
+ try {
27
+ const saved = await source.saveToFileAsync(uri, 'jpeg', 85);
28
+ if (!saved) throw new Error('Could not save the selected image to temporary storage');
29
+
30
+ const base64 = await source.toBase64StringAsync('jpeg', 85);
31
+ return { ...ref, dataUrl: `data:image/jpeg;base64,${base64}` };
32
+ } catch (error) {
33
+ files.release(ref);
34
+ throw error;
35
+ }
14
36
  },
15
37
  };
package/src/media.web.ts CHANGED
@@ -1,10 +1,30 @@
1
- // Media picking — web leaf. <input type=file accept="image/*"> → FileRef
2
- // (object URL). Multi-select stays out of v1.
3
- import type { FileRef } from './types';
1
+ // Media picking — web leaf. Keep an object URL for previews and a data URL for
2
+ // APIs that persist image payloads. Multi-select stays out of v1.
3
+ import type { PickedImage } from './types';
4
4
  import { files } from './files';
5
5
 
6
6
  export const media = {
7
- async pickImage(): Promise<FileRef | null> {
8
- return files.pick('image/*');
7
+ /** Opens the browser image picker; returns null when selection is canceled. */
8
+ async pickImage(): Promise<PickedImage | null> {
9
+ const file = await files.pick('image/*');
10
+ if (!file) return null;
11
+
12
+ try {
13
+ const blob = await (await fetch(file.uri)).blob();
14
+ const dataUrl = await new Promise<string>((resolve, reject) => {
15
+ const reader = new FileReader();
16
+ reader.onload = () => {
17
+ if (typeof reader.result === 'string') resolve(reader.result);
18
+ else reject(new Error('Could not read the selected image'));
19
+ };
20
+ reader.onerror = () =>
21
+ reject(reader.error ?? new Error('Could not read the selected image'));
22
+ reader.readAsDataURL(blob);
23
+ });
24
+ return { ...file, dataUrl };
25
+ } catch (error) {
26
+ files.release(file);
27
+ throw error;
28
+ }
9
29
  },
10
30
  };
package/src/types.ts CHANGED
@@ -40,6 +40,15 @@ export interface FileRef {
40
40
  uri: string;
41
41
  }
42
42
 
43
+ /**
44
+ * Result of `media.pickImage()`: a displayable URI and an upload data URL.
45
+ * Native URIs point to temporary JPEG files; call `files.release()` when the
46
+ * preview is no longer needed.
47
+ */
48
+ export interface PickedImage extends FileRef {
49
+ dataUrl: string;
50
+ }
51
+
43
52
  export interface Locale {
44
53
  tag: string;
45
54
  language: string;