@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 +12 -1
- package/src/clipboard.native.ts +27 -14
- package/src/clipboard.web.ts +27 -16
- package/src/files.native.ts +7 -1
- package/src/index.ts +14 -3
- package/src/media.native.ts +33 -11
- package/src/media.web.ts +25 -5
- package/src/types.ts +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octane-xplat/platform",
|
|
3
|
-
"version": "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"
|
package/src/clipboard.native.ts
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
import {
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
async function writeText(text: string): Promise<boolean> {
|
|
7
|
+
try {
|
|
8
|
+
Utils.copyToClipboard(text);
|
|
8
9
|
return true;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
};
|
package/src/clipboard.web.ts
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
|
-
// Clipboard —
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
};
|
package/src/files.native.ts
CHANGED
|
@@ -20,5 +20,11 @@ export const files = {
|
|
|
20
20
|
f.writeTextSync(text);
|
|
21
21
|
return { name, uri: p };
|
|
22
22
|
},
|
|
23
|
-
release(
|
|
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,
|
|
7
|
-
|
|
8
|
-
|
|
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';
|
package/src/media.native.ts
CHANGED
|
@@ -1,15 +1,37 @@
|
|
|
1
|
-
// Media picking —
|
|
2
|
-
//
|
|
3
|
-
import { create as createImagePicker } from '@nativescript/imagepicker';
|
|
4
|
-
import
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
if (!
|
|
12
|
-
|
|
13
|
-
|
|
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.
|
|
2
|
-
//
|
|
3
|
-
import type {
|
|
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
|
-
|
|
8
|
-
|
|
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;
|