@happyvertical/smrt-images 0.55.9 → 0.55.15
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/README.md +74 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +224 -2
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +77 -16
- package/dist/photo-cutout-coordinate-guide.d.ts +7 -0
- package/dist/photo-cutout-coordinate-guide.d.ts.map +1 -0
- package/dist/photo-cutout-profile-store.d.ts +76 -0
- package/dist/photo-cutout-profile-store.d.ts.map +1 -0
- package/dist/photo-cutout-setup.d.ts +48 -0
- package/dist/photo-cutout-setup.d.ts.map +1 -0
- package/dist/photo-cutout-setup.js +145 -0
- package/dist/photo-cutout-setup.js.map +1 -0
- package/dist/smrt-knowledge.json +36 -18
- package/dist/svelte/components/PhotoCutoutSetup.svelte +432 -0
- package/dist/svelte/components/PhotoCutoutSetup.svelte.d.ts +24 -0
- package/dist/svelte/components/PhotoCutoutSetup.svelte.d.ts.map +1 -0
- package/dist/svelte/head-isolation.d.ts +13 -0
- package/dist/svelte/head-isolation.d.ts.map +1 -0
- package/dist/svelte/head-isolation.js +83 -0
- package/dist/svelte/i18n.d.ts +18 -0
- package/dist/svelte/i18n.d.ts.map +1 -1
- package/dist/svelte/i18n.js +19 -0
- package/dist/svelte/index.d.ts +2 -1
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +2 -1
- package/package.json +15 -13
package/dist/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": "1.0.0",
|
|
3
3
|
"timestamp": 0,
|
|
4
4
|
"packageName": "@happyvertical/smrt-images",
|
|
5
|
-
"packageVersion": "0.55.
|
|
5
|
+
"packageVersion": "0.55.15",
|
|
6
6
|
"objects": {
|
|
7
7
|
"@happyvertical/smrt-images:Image": {
|
|
8
8
|
"name": "image",
|
|
@@ -39,68 +39,128 @@
|
|
|
39
39
|
},
|
|
40
40
|
"name": {
|
|
41
41
|
"type": "text",
|
|
42
|
-
"required": false
|
|
42
|
+
"required": false,
|
|
43
|
+
"description": "A recognizable name for this file or media item.",
|
|
44
|
+
"_meta": {
|
|
45
|
+
"description": "A recognizable name for this file or media item."
|
|
46
|
+
}
|
|
43
47
|
},
|
|
44
48
|
"sourceUri": {
|
|
45
49
|
"type": "text",
|
|
46
|
-
"required": false
|
|
50
|
+
"required": false,
|
|
51
|
+
"description": "Where the original file is stored or can be retrieved.",
|
|
52
|
+
"_meta": {
|
|
53
|
+
"description": "Where the original file is stored or can be retrieved."
|
|
54
|
+
}
|
|
47
55
|
},
|
|
48
56
|
"mimeType": {
|
|
49
57
|
"type": "text",
|
|
50
|
-
"required": false
|
|
58
|
+
"required": false,
|
|
59
|
+
"description": "The file format, such as image/jpeg or application/pdf.",
|
|
60
|
+
"_meta": {
|
|
61
|
+
"description": "The file format, such as image/jpeg or application/pdf."
|
|
62
|
+
}
|
|
51
63
|
},
|
|
52
64
|
"description": {
|
|
53
65
|
"type": "text",
|
|
54
|
-
"required": false
|
|
66
|
+
"required": false,
|
|
67
|
+
"description": "Explain what the file contains and when it should be used.",
|
|
68
|
+
"_meta": {
|
|
69
|
+
"description": "Explain what the file contains and when it should be used."
|
|
70
|
+
}
|
|
55
71
|
},
|
|
56
72
|
"metadata": {
|
|
57
73
|
"type": "text",
|
|
58
|
-
"required": false
|
|
74
|
+
"required": false,
|
|
75
|
+
"description": "Additional details recorded while processing this file.",
|
|
76
|
+
"_meta": {
|
|
77
|
+
"description": "Additional details recorded while processing this file."
|
|
78
|
+
}
|
|
59
79
|
},
|
|
60
80
|
"version": {
|
|
61
81
|
"type": "integer",
|
|
62
82
|
"required": true,
|
|
63
|
-
"default": 1
|
|
83
|
+
"default": 1,
|
|
84
|
+
"description": "The revision number of this file in its version history.",
|
|
85
|
+
"_meta": {
|
|
86
|
+
"description": "The revision number of this file in its version history."
|
|
87
|
+
}
|
|
64
88
|
},
|
|
65
89
|
"primaryVersionId": {
|
|
66
90
|
"type": "foreignKey",
|
|
67
91
|
"required": false,
|
|
68
|
-
"related": "Asset"
|
|
92
|
+
"related": "Asset",
|
|
93
|
+
"description": "The first version of this file, linking its revision history.",
|
|
94
|
+
"_meta": {
|
|
95
|
+
"description": "The first version of this file, linking its revision history."
|
|
96
|
+
}
|
|
69
97
|
},
|
|
70
98
|
"typeSlug": {
|
|
71
99
|
"type": "text",
|
|
72
|
-
"required": false
|
|
100
|
+
"required": false,
|
|
101
|
+
"description": "The category of asset, such as an image, video, or document.",
|
|
102
|
+
"_meta": {
|
|
103
|
+
"description": "The category of asset, such as an image, video, or document."
|
|
104
|
+
}
|
|
73
105
|
},
|
|
74
106
|
"statusSlug": {
|
|
75
107
|
"type": "text",
|
|
76
|
-
"required": false
|
|
108
|
+
"required": false,
|
|
109
|
+
"description": "The stage of this asset, such as draft, published, or archived.",
|
|
110
|
+
"_meta": {
|
|
111
|
+
"description": "The stage of this asset, such as draft, published, or archived."
|
|
112
|
+
}
|
|
77
113
|
},
|
|
78
114
|
"ownerProfileId": {
|
|
79
115
|
"type": "crossPackageRef",
|
|
80
116
|
"required": false,
|
|
81
|
-
"related": "@happyvertical/smrt-profiles:Profile"
|
|
117
|
+
"related": "@happyvertical/smrt-profiles:Profile",
|
|
118
|
+
"description": "The person or organization responsible for this asset.",
|
|
119
|
+
"_meta": {
|
|
120
|
+
"description": "The person or organization responsible for this asset."
|
|
121
|
+
}
|
|
82
122
|
},
|
|
83
123
|
"sourceAssetId": {
|
|
84
124
|
"type": "foreignKey",
|
|
85
125
|
"required": false,
|
|
86
|
-
"related": "Asset"
|
|
126
|
+
"related": "Asset",
|
|
127
|
+
"description": "The original asset used to produce this file or variation.",
|
|
128
|
+
"_meta": {
|
|
129
|
+
"description": "The original asset used to produce this file or variation."
|
|
130
|
+
}
|
|
87
131
|
},
|
|
88
132
|
"folderId": {
|
|
89
133
|
"type": "foreignKey",
|
|
90
134
|
"required": false,
|
|
91
|
-
"related": "Folder"
|
|
135
|
+
"related": "Folder",
|
|
136
|
+
"description": "The folder that organizes this file in the asset library.",
|
|
137
|
+
"_meta": {
|
|
138
|
+
"description": "The folder that organizes this file in the asset library."
|
|
139
|
+
}
|
|
92
140
|
},
|
|
93
141
|
"sourceType": {
|
|
94
142
|
"type": "text",
|
|
95
|
-
"required": false
|
|
143
|
+
"required": false,
|
|
144
|
+
"description": "The service or source from which this file was obtained.",
|
|
145
|
+
"_meta": {
|
|
146
|
+
"description": "The service or source from which this file was obtained."
|
|
147
|
+
}
|
|
96
148
|
},
|
|
97
149
|
"externalId": {
|
|
98
150
|
"type": "text",
|
|
99
|
-
"required": false
|
|
151
|
+
"required": false,
|
|
152
|
+
"description": "The reference used to identify this file at its original source.",
|
|
153
|
+
"_meta": {
|
|
154
|
+
"description": "The reference used to identify this file at its original source."
|
|
155
|
+
}
|
|
100
156
|
},
|
|
101
157
|
"externalRefs": {
|
|
102
158
|
"type": "text",
|
|
103
|
-
"required": false
|
|
159
|
+
"required": false,
|
|
160
|
+
"description": "References that connect this file to copies held by other services.",
|
|
161
|
+
"_meta": {
|
|
162
|
+
"description": "References that connect this file to copies held by other services."
|
|
163
|
+
}
|
|
104
164
|
},
|
|
105
165
|
"createdAt": {
|
|
106
166
|
"type": "text",
|
|
@@ -916,6 +976,7 @@
|
|
|
916
976
|
"smrtDependencies": [
|
|
917
977
|
"@happyvertical/smrt-assets",
|
|
918
978
|
"@happyvertical/smrt-core",
|
|
979
|
+
"@happyvertical/smrt-profiles",
|
|
919
980
|
"@happyvertical/smrt-prompts",
|
|
920
981
|
"@happyvertical/smrt-tenancy"
|
|
921
982
|
],
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Produces a transient grid-overlay copy for a vision request. The source
|
|
3
|
+
* image remains the asset used by the browser cutout; this guide is never
|
|
4
|
+
* saved or returned to a client.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createPhotoCutoutCoordinateGuide(sourceDataUrl: string, width: number, height: number): Promise<string>;
|
|
7
|
+
//# sourceMappingURL=photo-cutout-coordinate-guide.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"photo-cutout-coordinate-guide.d.ts","sourceRoot":"","sources":["../src/photo-cutout-coordinate-guide.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAsB,gCAAgC,CACpD,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAqCjB"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type PhotoCutoutRig } from '@happyvertical/animation';
|
|
2
|
+
import type { Asset, AssetRuntimeLike } from '@happyvertical/smrt-assets';
|
|
3
|
+
/** Stable rig key resolved by the host to the persisted asset bytes. */
|
|
4
|
+
export declare const PHOTO_CUTOUT_PERSISTED_ASSET_REF = "character_cutout";
|
|
5
|
+
export interface PhotoCutoutProfileOwner {
|
|
6
|
+
id?: string;
|
|
7
|
+
tenantId?: string | null;
|
|
8
|
+
getAssets(relationship?: string): Promise<Asset[]>;
|
|
9
|
+
addAsset(asset: Asset, relationship?: string, sortOrder?: number): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export interface PhotoCutoutProfileStoreAuthorization {
|
|
12
|
+
actorProfileId: string;
|
|
13
|
+
profile: PhotoCutoutProfileOwner;
|
|
14
|
+
tenantId: string | null;
|
|
15
|
+
operation: 'load' | 'save';
|
|
16
|
+
}
|
|
17
|
+
export interface PhotoCutoutProfileStoreOptions {
|
|
18
|
+
runtime: AssetRuntimeLike;
|
|
19
|
+
getProfile(profileId: string): Promise<PhotoCutoutProfileOwner | null>;
|
|
20
|
+
/** The host verifies its authenticated principal and any app-specific grants. */
|
|
21
|
+
authorize(request: PhotoCutoutProfileStoreAuthorization): Promise<boolean> | boolean;
|
|
22
|
+
now?: () => Date;
|
|
23
|
+
}
|
|
24
|
+
export interface SavePhotoCutoutProfileSetupInput {
|
|
25
|
+
actorProfileId: string;
|
|
26
|
+
profileId: string;
|
|
27
|
+
tenantId: string | null;
|
|
28
|
+
png: Buffer;
|
|
29
|
+
rig: PhotoCutoutRig;
|
|
30
|
+
name?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface LoadPhotoCutoutProfileSetupInput {
|
|
33
|
+
actorProfileId: string;
|
|
34
|
+
profileId: string;
|
|
35
|
+
tenantId: string | null;
|
|
36
|
+
/**
|
|
37
|
+
* A photo-cutout asset already linked to this profile. Omitting this keeps
|
|
38
|
+
* the original behavior of loading the most recently saved setup.
|
|
39
|
+
*/
|
|
40
|
+
assetId?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface PersistedPhotoCutoutSetup {
|
|
43
|
+
assetId: string;
|
|
44
|
+
rig: PhotoCutoutRig;
|
|
45
|
+
png: Buffer;
|
|
46
|
+
savedAt: string;
|
|
47
|
+
}
|
|
48
|
+
/** Metadata for an authorized saved setup, without reading its PNG bytes. */
|
|
49
|
+
export interface SavedPhotoCutoutProfileSetup {
|
|
50
|
+
assetId: string;
|
|
51
|
+
savedAt: string;
|
|
52
|
+
name?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Persists one profile-owned photographic character setup using the canonical
|
|
56
|
+
* AssetRuntime bytes and ProfileAsset link. Authentication stays with the host;
|
|
57
|
+
* this service refuses cross-profile and cross-tenant calls before it writes.
|
|
58
|
+
*/
|
|
59
|
+
export declare class PhotoCutoutProfileStore {
|
|
60
|
+
private readonly options;
|
|
61
|
+
private readonly now;
|
|
62
|
+
constructor(options: PhotoCutoutProfileStoreOptions);
|
|
63
|
+
save(input: SavePhotoCutoutProfileSetupInput): Promise<PersistedPhotoCutoutSetup>;
|
|
64
|
+
load(input: LoadPhotoCutoutProfileSetupInput): Promise<PersistedPhotoCutoutSetup | null>;
|
|
65
|
+
private loadSelected;
|
|
66
|
+
/**
|
|
67
|
+
* Lists valid, profile-linked saved setups without fetching their image
|
|
68
|
+
* bytes. Authorization deliberately follows `load`: gallery visibility is
|
|
69
|
+
* the same protected read as selecting a setup.
|
|
70
|
+
*/
|
|
71
|
+
list(input: LoadPhotoCutoutProfileSetupInput): Promise<SavedPhotoCutoutProfileSetup[]>;
|
|
72
|
+
private readSaved;
|
|
73
|
+
private readLinkedCandidates;
|
|
74
|
+
private requireAuthorizedProfile;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=photo-cutout-profile-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"photo-cutout-profile-store.d.ts","sourceRoot":"","sources":["../src/photo-cutout-profile-store.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,cAAc,EAEpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAK1E,wEAAwE;AACxE,eAAO,MAAM,gCAAgC,qBAAqB,CAAC;AAEnE,MAAM,WAAW,uBAAuB;IACtC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACnD,QAAQ,CACN,KAAK,EAAE,KAAK,EACZ,YAAY,CAAC,EAAE,MAAM,EACrB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB;AAED,MAAM,WAAW,oCAAoC;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,uBAAuB,CAAC;IACjC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,gBAAgB,CAAC;IAC1B,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAAC;IACvE,iFAAiF;IACjF,SAAS,CACP,OAAO,EAAE,oCAAoC,GAC5C,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,gCAAgC;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,cAAc,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gCAAgC;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,cAAc,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,6EAA6E;AAC7E,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAUD;;;;GAIG;AACH,qBAAa,uBAAuB;IAGtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAa;gBAEJ,OAAO,EAAE,8BAA8B;IAI9D,IAAI,CACR,KAAK,EAAE,gCAAgC,GACtC,OAAO,CAAC,yBAAyB,CAAC;IA4D/B,IAAI,CACR,KAAK,EAAE,gCAAgC,GACtC,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC;YAc9B,YAAY;IAe1B;;;;OAIG;IACG,IAAI,CACR,KAAK,EAAE,gCAAgC,GACtC,OAAO,CAAC,4BAA4B,EAAE,CAAC;YAW5B,SAAS;IA0BvB,OAAO,CAAC,oBAAoB;YAmBd,wBAAwB;CAqBvC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type PhotoCutoutRig } from '@happyvertical/animation';
|
|
2
|
+
/** A small, server-safe boundary for untrusted vision-model JSON. */
|
|
3
|
+
export interface PhotoCutoutSetupInput {
|
|
4
|
+
assetId: string;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
}
|
|
8
|
+
export interface FaceOutline {
|
|
9
|
+
points: Array<{
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
export interface MouthLandmarks {
|
|
15
|
+
mouthLeft: {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
};
|
|
19
|
+
mouthRight: {
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
};
|
|
23
|
+
chin: {
|
|
24
|
+
x: number;
|
|
25
|
+
y: number;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** Validate a normalized (0..1000) closed face silhouette from the vision pass. */
|
|
29
|
+
export declare function parseFaceOutline(response: string | unknown): FaceOutline;
|
|
30
|
+
/** Build the Canadian horizontal split from real cropped-face landmarks. */
|
|
31
|
+
export declare function assembleCanadianSplitRig(input: {
|
|
32
|
+
outline: FaceOutline;
|
|
33
|
+
landmarks: MouthLandmarks;
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
assetId: string;
|
|
37
|
+
}): PhotoCutoutRig;
|
|
38
|
+
export declare function faceOutlinePrompt(): string;
|
|
39
|
+
export declare function parseMouthLandmarks(response: string | unknown): MouthLandmarks;
|
|
40
|
+
export declare function mouthLandmarksPrompt(): string;
|
|
41
|
+
/**
|
|
42
|
+
* Parses a model response and binds every image layer to the authorized source
|
|
43
|
+
* image. The animation validator is the final geometry and bounds authority.
|
|
44
|
+
*/
|
|
45
|
+
export declare function parsePhotoCutoutSetup(response: string | unknown, source: PhotoCutoutSetupInput): PhotoCutoutRig;
|
|
46
|
+
/** Deliberately specific instructions reduce geometry ambiguity from vision models. */
|
|
47
|
+
export declare function photoCutoutSetupPrompt(source: PhotoCutoutSetupInput): string;
|
|
48
|
+
//# sourceMappingURL=photo-cutout-setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"photo-cutout-setup.d.ts","sourceRoot":"","sources":["../src/photo-cutout-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAIpB,MAAM,0BAA0B,CAAC;AAElC,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzC;AACD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,UAAU,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAChC;AAED,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,CAgCxE;AAED,4EAA4E;AAC5E,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,cAAc,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,cAAc,CAsDjB;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,GAAG,OAAO,GACzB,cAAc,CAkChB;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AASD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GAAG,OAAO,EAC1B,MAAM,EAAE,qBAAqB,GAC5B,cAAc,CA2ChB;AAED,uFAAuF;AACvF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAI5E"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { PhotoCutoutValidationError, clipPhotoCutoutOutlineBelowLine, validatePhotoCutoutOutline, validatePhotoCutoutRig } from "@happyvertical/animation";
|
|
2
|
+
//#region src/photo-cutout-setup.ts
|
|
3
|
+
function parseFaceOutline(response) {
|
|
4
|
+
const points = record(typeof response === "string" ? JSON.parse(response) : response).points;
|
|
5
|
+
if (!Array.isArray(points) || points.length < 5 || points.length > 64) throw new PhotoCutoutValidationError("face outline needs 5-64 points");
|
|
6
|
+
return { points: validatePhotoCutoutOutline(points.map((point) => {
|
|
7
|
+
const p = record(point);
|
|
8
|
+
const x = p.x;
|
|
9
|
+
const y = p.y;
|
|
10
|
+
if (typeof x !== "number" || typeof y !== "number" || !Number.isFinite(x) || !Number.isFinite(y) || x < 0 || y < 0 || x > 1e3 || y > 1e3) throw new PhotoCutoutValidationError("face outline point is outside normalized bounds");
|
|
11
|
+
return {
|
|
12
|
+
x,
|
|
13
|
+
y
|
|
14
|
+
};
|
|
15
|
+
}), {
|
|
16
|
+
width: 1e3,
|
|
17
|
+
height: 1e3,
|
|
18
|
+
minPoints: 5
|
|
19
|
+
}) };
|
|
20
|
+
}
|
|
21
|
+
function assembleCanadianSplitRig(input) {
|
|
22
|
+
const seam = (input.landmarks.mouthLeft.y + input.landmarks.mouthRight.y) / 2;
|
|
23
|
+
const lower = clipPhotoCutoutOutlineBelowLine(input.outline.points, {
|
|
24
|
+
width: 1e3,
|
|
25
|
+
height: 1e3,
|
|
26
|
+
y: seam,
|
|
27
|
+
minPoints: 3
|
|
28
|
+
});
|
|
29
|
+
const scale = (p) => ({
|
|
30
|
+
x: p.x / 1e3 * input.width,
|
|
31
|
+
y: p.y / 1e3 * input.height
|
|
32
|
+
});
|
|
33
|
+
const head = input.outline.points.map(scale);
|
|
34
|
+
const jaw = lower.map(scale);
|
|
35
|
+
const gap = Math.max(2, Math.round((input.landmarks.chin.y - seam) / 1e3 * input.height * .36));
|
|
36
|
+
const canvasHeight = input.height + gap;
|
|
37
|
+
const pivot = scale({
|
|
38
|
+
x: (input.landmarks.mouthLeft.x + input.landmarks.mouthRight.x) / 2,
|
|
39
|
+
y: seam
|
|
40
|
+
});
|
|
41
|
+
const rig = {
|
|
42
|
+
rigKind: "photo-cutout",
|
|
43
|
+
version: 1,
|
|
44
|
+
id: "portrait",
|
|
45
|
+
ariaLabel: "Photographic Canadian character",
|
|
46
|
+
canvas: {
|
|
47
|
+
width: input.width,
|
|
48
|
+
height: canvasHeight
|
|
49
|
+
},
|
|
50
|
+
layers: [{
|
|
51
|
+
id: "head",
|
|
52
|
+
assetId: input.assetId,
|
|
53
|
+
role: "head",
|
|
54
|
+
clip: head,
|
|
55
|
+
imageFrame: {
|
|
56
|
+
x: 0,
|
|
57
|
+
y: 0,
|
|
58
|
+
width: input.width,
|
|
59
|
+
height: input.height
|
|
60
|
+
}
|
|
61
|
+
}, {
|
|
62
|
+
id: "jaw",
|
|
63
|
+
assetId: input.assetId,
|
|
64
|
+
role: "jaw",
|
|
65
|
+
clip: jaw,
|
|
66
|
+
imageFrame: {
|
|
67
|
+
x: 0,
|
|
68
|
+
y: 0,
|
|
69
|
+
width: input.width,
|
|
70
|
+
height: input.height
|
|
71
|
+
}
|
|
72
|
+
}],
|
|
73
|
+
jaw: {
|
|
74
|
+
layerId: "jaw",
|
|
75
|
+
pivot,
|
|
76
|
+
maxOpenDegrees: 0,
|
|
77
|
+
maxOpenOffset: {
|
|
78
|
+
x: 0,
|
|
79
|
+
y: gap
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
validatePhotoCutoutRig(rig);
|
|
84
|
+
return rig;
|
|
85
|
+
}
|
|
86
|
+
function faceOutlinePrompt() {
|
|
87
|
+
return "Return JSON only: {\"points\":[{\"x\":number,\"y\":number},...]}. The first image is the original portrait. The second is the same original pixels with a labeled coordinate grid: labels 0..1000 map directly to this response’s x and y coordinates. Use that grid to place every point. Trace the visible head-and-face silhouette tightly in clockwise order, starting at the highest visible hair point. Include an actual point at the outermost left hair/ear edge, outermost right hair/ear edge, and lowest chin point; trace every visible hair, ear, cheek, and chin boundary between them without jumping through background. Exclude background beside hair and ears. Use 24-48 points where needed for curved hair/ear contours. This is an approximate closed silhouette for transparent background removal, not fine-hair matting and never a rectangular crop. No prose or extra keys.";
|
|
88
|
+
}
|
|
89
|
+
function parseMouthLandmarks(response) {
|
|
90
|
+
const root = record(typeof response === "string" ? JSON.parse(response) : response);
|
|
91
|
+
const point = (name) => {
|
|
92
|
+
const p = record(root[name]);
|
|
93
|
+
const x = p.x;
|
|
94
|
+
const y = p.y;
|
|
95
|
+
if (typeof x !== "number" || typeof y !== "number" || !Number.isFinite(x) || !Number.isFinite(y) || x < 0 || x > 1e3 || y < 0 || y > 1e3) throw new PhotoCutoutValidationError(`${name} is outside normalized bounds`);
|
|
96
|
+
return {
|
|
97
|
+
x,
|
|
98
|
+
y
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
const mouthLeft = point("mouthLeft");
|
|
102
|
+
const mouthRight = point("mouthRight");
|
|
103
|
+
const chin = point("chin");
|
|
104
|
+
if (mouthLeft.x >= mouthRight.x || chin.y <= Math.max(mouthLeft.y, mouthRight.y)) throw new PhotoCutoutValidationError("mouth and chin landmarks are anatomically invalid");
|
|
105
|
+
return {
|
|
106
|
+
mouthLeft,
|
|
107
|
+
mouthRight,
|
|
108
|
+
chin
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function mouthLandmarksPrompt() {
|
|
112
|
+
return "Return JSON only: {\"mouthLeft\":{\"x\":number,\"y\":number},\"mouthRight\":{\"x\":number,\"y\":number},\"chin\":{\"x\":number,\"y\":number}}. Analyze this transparent cropped face. Coordinates are normalized 0..1000 in this cropped image. mouthLeft and mouthRight are the actual outer lip corners; chin is the center lowest chin point. Do not return polygons, a rig, or prose.";
|
|
113
|
+
}
|
|
114
|
+
function record(value) {
|
|
115
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new PhotoCutoutValidationError("setup response must be an object");
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
function parsePhotoCutoutSetup(response, source) {
|
|
119
|
+
const candidate = typeof response === "string" ? JSON.parse(response) : response;
|
|
120
|
+
const rig = record(candidate).rig ?? candidate;
|
|
121
|
+
const rigRecord = record(rig);
|
|
122
|
+
const canvas = record(rigRecord.canvas);
|
|
123
|
+
if (canvas.width !== source.width || canvas.height !== source.height) throw new PhotoCutoutValidationError("setup canvas does not match source image");
|
|
124
|
+
const layers = rigRecord.layers;
|
|
125
|
+
if (!Array.isArray(layers)) throw new PhotoCutoutValidationError("setup layers are required");
|
|
126
|
+
for (const layer of layers) {
|
|
127
|
+
const layerRecord = record(layer);
|
|
128
|
+
if (layerRecord.kind !== "solid" && layerRecord.assetId !== source.assetId) throw new PhotoCutoutValidationError("setup may reference only the authorized source asset");
|
|
129
|
+
}
|
|
130
|
+
const jaw = layers.find((layer) => record(layer).role === "jaw");
|
|
131
|
+
const mouth = layers.find((layer) => record(layer).role === "mouth-interior" && record(layer).kind === "solid");
|
|
132
|
+
if (!jaw || !mouth) throw new PhotoCutoutValidationError("setup requires jaw and mouth-interior layers");
|
|
133
|
+
record(mouth).clip = record(jaw).clip;
|
|
134
|
+
validatePhotoCutoutRig(rig);
|
|
135
|
+
return rig;
|
|
136
|
+
}
|
|
137
|
+
function photoCutoutSetupPrompt(source) {
|
|
138
|
+
return `Analyze the supplied portrait and return JSON only with this exact shape:
|
|
139
|
+
{"rig":{"rigKind":"photo-cutout","version":1,"id":"portrait","ariaLabel":"Photographic portrait","canvas":{"width":${source.width},"height":${source.height}},"layers":[{"id":"head","assetId":"${source.assetId}","role":"head","clip":[{"x":10,"y":10},{"x":90,"y":10},{"x":90,"y":110},{"x":10,"y":110}]},{"id":"mouth","kind":"solid","role":"mouth-interior","color":{"r":30,"g":10,"b":10},"clip":[{"x":40,"y":70},{"x":60,"y":70},{"x":50,"y":82}]},{"id":"jaw","assetId":"${source.assetId}","role":"jaw","clip":[{"x":30,"y":65},{"x":70,"y":65},{"x":70,"y":105},{"x":30,"y":105}]}],"jaw":{"layerId":"jaw","pivot":{"x":50,"y":65},"maxOpenDegrees":12}}}
|
|
140
|
+
Use the example only as a schema: scale all numeric coordinates to the actual ${source.width}x${source.height} image. Image layers use assetId "${source.assetId}"; the only solid layer is mouth-interior and uses integer RGB. Create a cutout head polygon, jaw polygon, and visible mouth interior from the photographed face. The jaw must be an inset lower-face polygon: leave at least 2% of head width/height between every jaw or pivot point and the head boundary. Place mouth entirely inside jaw. Every polygon must be simple, have at least three points, remain in the canvas, and the jaw/mouth must be inside the head. Do not include URLs, SVG paths, markup, transforms, prose, or keys not shown.`;
|
|
141
|
+
}
|
|
142
|
+
//#endregion
|
|
143
|
+
export { assembleCanadianSplitRig, faceOutlinePrompt, mouthLandmarksPrompt, parseFaceOutline, parseMouthLandmarks, parsePhotoCutoutSetup, photoCutoutSetupPrompt };
|
|
144
|
+
|
|
145
|
+
//# sourceMappingURL=photo-cutout-setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"photo-cutout-setup.js","names":[],"sources":["../src/photo-cutout-setup.ts"],"sourcesContent":["import {\n clipPhotoCutoutOutlineBelowLine,\n type PhotoCutoutRig,\n PhotoCutoutValidationError,\n validatePhotoCutoutOutline,\n validatePhotoCutoutRig,\n} from '@happyvertical/animation';\n\n/** A small, server-safe boundary for untrusted vision-model JSON. */\nexport interface PhotoCutoutSetupInput {\n assetId: string;\n width: number;\n height: number;\n}\n\nexport interface FaceOutline {\n points: Array<{ x: number; y: number }>;\n}\nexport interface MouthLandmarks {\n mouthLeft: { x: number; y: number };\n mouthRight: { x: number; y: number };\n chin: { x: number; y: number };\n}\n\n/** Validate a normalized (0..1000) closed face silhouette from the vision pass. */\nexport function parseFaceOutline(response: string | unknown): FaceOutline {\n const value = typeof response === 'string' ? JSON.parse(response) : response;\n const points = record(value).points;\n if (!Array.isArray(points) || points.length < 5 || points.length > 64) {\n throw new PhotoCutoutValidationError('face outline needs 5-64 points');\n }\n const parsed = points.map((point) => {\n const p = record(point);\n const x = p.x;\n const y = p.y;\n if (\n typeof x !== 'number' ||\n typeof y !== 'number' ||\n !Number.isFinite(x) ||\n !Number.isFinite(y) ||\n x < 0 ||\n y < 0 ||\n x > 1000 ||\n y > 1000\n )\n throw new PhotoCutoutValidationError(\n 'face outline point is outside normalized bounds',\n );\n return { x, y };\n });\n return {\n points: validatePhotoCutoutOutline(parsed, {\n width: 1000,\n height: 1000,\n minPoints: 5,\n }),\n };\n}\n\n/** Build the Canadian horizontal split from real cropped-face landmarks. */\nexport function assembleCanadianSplitRig(input: {\n outline: FaceOutline;\n landmarks: MouthLandmarks;\n width: number;\n height: number;\n assetId: string;\n}): PhotoCutoutRig {\n const seam = (input.landmarks.mouthLeft.y + input.landmarks.mouthRight.y) / 2;\n const lower = clipPhotoCutoutOutlineBelowLine(input.outline.points, {\n width: 1000,\n height: 1000,\n y: seam,\n minPoints: 3,\n });\n const scale = (p: { x: number; y: number }) => ({\n x: (p.x / 1000) * input.width,\n y: (p.y / 1000) * input.height,\n });\n const head = input.outline.points.map(scale);\n const jaw = lower.map(scale);\n const gap = Math.max(\n 2,\n Math.round(((input.landmarks.chin.y - seam) / 1000) * input.height * 0.36),\n );\n const canvasHeight = input.height + gap;\n const pivot = scale({\n x: (input.landmarks.mouthLeft.x + input.landmarks.mouthRight.x) / 2,\n y: seam,\n });\n const rig: PhotoCutoutRig = {\n rigKind: 'photo-cutout',\n version: 1,\n id: 'portrait',\n ariaLabel: 'Photographic Canadian character',\n canvas: { width: input.width, height: canvasHeight },\n layers: [\n {\n id: 'head',\n assetId: input.assetId,\n role: 'head',\n clip: head,\n imageFrame: { x: 0, y: 0, width: input.width, height: input.height },\n },\n {\n id: 'jaw',\n assetId: input.assetId,\n role: 'jaw',\n clip: jaw,\n imageFrame: { x: 0, y: 0, width: input.width, height: input.height },\n },\n ],\n jaw: {\n layerId: 'jaw',\n pivot,\n maxOpenDegrees: 0,\n maxOpenOffset: { x: 0, y: gap },\n },\n };\n validatePhotoCutoutRig(rig);\n return rig;\n}\n\nexport function faceOutlinePrompt(): string {\n return 'Return JSON only: {\"points\":[{\"x\":number,\"y\":number},...]}. The first image is the original portrait. The second is the same original pixels with a labeled coordinate grid: labels 0..1000 map directly to this response’s x and y coordinates. Use that grid to place every point. Trace the visible head-and-face silhouette tightly in clockwise order, starting at the highest visible hair point. Include an actual point at the outermost left hair/ear edge, outermost right hair/ear edge, and lowest chin point; trace every visible hair, ear, cheek, and chin boundary between them without jumping through background. Exclude background beside hair and ears. Use 24-48 points where needed for curved hair/ear contours. This is an approximate closed silhouette for transparent background removal, not fine-hair matting and never a rectangular crop. No prose or extra keys.';\n}\n\nexport function parseMouthLandmarks(\n response: string | unknown,\n): MouthLandmarks {\n const root = record(\n typeof response === 'string' ? JSON.parse(response) : response,\n );\n const point = (name: 'mouthLeft' | 'mouthRight' | 'chin') => {\n const p = record(root[name]);\n const x = p.x;\n const y = p.y;\n if (\n typeof x !== 'number' ||\n typeof y !== 'number' ||\n !Number.isFinite(x) ||\n !Number.isFinite(y) ||\n x < 0 ||\n x > 1000 ||\n y < 0 ||\n y > 1000\n )\n throw new PhotoCutoutValidationError(\n `${name} is outside normalized bounds`,\n );\n return { x, y };\n };\n const mouthLeft = point('mouthLeft');\n const mouthRight = point('mouthRight');\n const chin = point('chin');\n if (\n mouthLeft.x >= mouthRight.x ||\n chin.y <= Math.max(mouthLeft.y, mouthRight.y)\n )\n throw new PhotoCutoutValidationError(\n 'mouth and chin landmarks are anatomically invalid',\n );\n return { mouthLeft, mouthRight, chin };\n}\n\nexport function mouthLandmarksPrompt(): string {\n return 'Return JSON only: {\"mouthLeft\":{\"x\":number,\"y\":number},\"mouthRight\":{\"x\":number,\"y\":number},\"chin\":{\"x\":number,\"y\":number}}. Analyze this transparent cropped face. Coordinates are normalized 0..1000 in this cropped image. mouthLeft and mouthRight are the actual outer lip corners; chin is the center lowest chin point. Do not return polygons, a rig, or prose.';\n}\n\nfunction record(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new PhotoCutoutValidationError('setup response must be an object');\n }\n return value as Record<string, unknown>;\n}\n\n/**\n * Parses a model response and binds every image layer to the authorized source\n * image. The animation validator is the final geometry and bounds authority.\n */\nexport function parsePhotoCutoutSetup(\n response: string | unknown,\n source: PhotoCutoutSetupInput,\n): PhotoCutoutRig {\n const candidate =\n typeof response === 'string' ? JSON.parse(response) : response;\n const root = record(candidate);\n const rig = (root.rig ?? candidate) as unknown;\n const rigRecord = record(rig);\n const canvas = record(rigRecord.canvas);\n if (canvas.width !== source.width || canvas.height !== source.height) {\n throw new PhotoCutoutValidationError(\n 'setup canvas does not match source image',\n );\n }\n const layers = rigRecord.layers;\n if (!Array.isArray(layers)) {\n throw new PhotoCutoutValidationError('setup layers are required');\n }\n for (const layer of layers) {\n const layerRecord = record(layer);\n if (\n layerRecord.kind !== 'solid' &&\n layerRecord.assetId !== source.assetId\n ) {\n throw new PhotoCutoutValidationError(\n 'setup may reference only the authorized source asset',\n );\n }\n }\n // The solid mouth is painted beneath the photographic jaw. Making its clip\n // identical to the jaw guarantees it cannot appear through a neutral jaw;\n // the runtime reveals it only as the jaw rotates open.\n const jaw = layers.find((layer) => record(layer).role === 'jaw');\n const mouth = layers.find(\n (layer) =>\n record(layer).role === 'mouth-interior' && record(layer).kind === 'solid',\n );\n if (!jaw || !mouth) {\n throw new PhotoCutoutValidationError(\n 'setup requires jaw and mouth-interior layers',\n );\n }\n record(mouth).clip = record(jaw).clip;\n validatePhotoCutoutRig(rig);\n return rig;\n}\n\n/** Deliberately specific instructions reduce geometry ambiguity from vision models. */\nexport function photoCutoutSetupPrompt(source: PhotoCutoutSetupInput): string {\n return `Analyze the supplied portrait and return JSON only with this exact shape:\n{\"rig\":{\"rigKind\":\"photo-cutout\",\"version\":1,\"id\":\"portrait\",\"ariaLabel\":\"Photographic portrait\",\"canvas\":{\"width\":${source.width},\"height\":${source.height}},\"layers\":[{\"id\":\"head\",\"assetId\":\"${source.assetId}\",\"role\":\"head\",\"clip\":[{\"x\":10,\"y\":10},{\"x\":90,\"y\":10},{\"x\":90,\"y\":110},{\"x\":10,\"y\":110}]},{\"id\":\"mouth\",\"kind\":\"solid\",\"role\":\"mouth-interior\",\"color\":{\"r\":30,\"g\":10,\"b\":10},\"clip\":[{\"x\":40,\"y\":70},{\"x\":60,\"y\":70},{\"x\":50,\"y\":82}]},{\"id\":\"jaw\",\"assetId\":\"${source.assetId}\",\"role\":\"jaw\",\"clip\":[{\"x\":30,\"y\":65},{\"x\":70,\"y\":65},{\"x\":70,\"y\":105},{\"x\":30,\"y\":105}]}],\"jaw\":{\"layerId\":\"jaw\",\"pivot\":{\"x\":50,\"y\":65},\"maxOpenDegrees\":12}}}\nUse the example only as a schema: scale all numeric coordinates to the actual ${source.width}x${source.height} image. Image layers use assetId \"${source.assetId}\"; the only solid layer is mouth-interior and uses integer RGB. Create a cutout head polygon, jaw polygon, and visible mouth interior from the photographed face. The jaw must be an inset lower-face polygon: leave at least 2% of head width/height between every jaw or pivot point and the head boundary. Place mouth entirely inside jaw. Every polygon must be simple, have at least three points, remain in the canvas, and the jaw/mouth must be inside the head. Do not include URLs, SVG paths, markup, transforms, prose, or keys not shown.`;\n}\n"],"mappings":"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"}
|
package/dist/smrt-knowledge.json
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
"sensitiveFieldsExcluded": true,
|
|
4
4
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
5
5
|
"packageName": "@happyvertical/smrt-images",
|
|
6
|
-
"packageVersion": "0.55.
|
|
6
|
+
"packageVersion": "0.55.15",
|
|
7
7
|
"sourceManifestPath": "dist/manifest.json",
|
|
8
8
|
"agentDocPath": "AGENTS.md",
|
|
9
9
|
"sourceHashes": {
|
|
10
|
-
"manifest": "
|
|
11
|
-
"packageJson": "
|
|
10
|
+
"manifest": "e6aa5cd35b14bdfc34077c2caba78899e1f8daf47ff5eb2b95b9ca0e9535a985",
|
|
11
|
+
"packageJson": "d7707f46ba2840704411112fff5272d98aad2f0f13c39506451bd43bdfc9f251",
|
|
12
12
|
"agents": "82a1905b76f49d9a852bf1e4f591595ae596c5cfa14ec736dc6f8fe32200fd8c"
|
|
13
13
|
},
|
|
14
14
|
"exports": [
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"./workbench"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
+
"@happyvertical/animation": "0.5.0",
|
|
24
25
|
"@happyvertical/ai": "catalog:",
|
|
25
26
|
"@happyvertical/images": "catalog:",
|
|
26
27
|
"@happyvertical/smrt-assets": "workspace:*",
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
"jimp": "catalog:",
|
|
34
35
|
"sharp": "catalog:",
|
|
35
36
|
"@happyvertical/smrt-playground": "workspace:*",
|
|
37
|
+
"@happyvertical/smrt-profiles": "workspace:*",
|
|
36
38
|
"@happyvertical/smrt-vitest": "workspace:*",
|
|
37
39
|
"@happyvertical/sql": "catalog:",
|
|
38
40
|
"@sveltejs/kit": "2.69.1",
|
|
@@ -49,6 +51,7 @@
|
|
|
49
51
|
"@happyvertical/smrt-assets",
|
|
50
52
|
"@happyvertical/smrt-core",
|
|
51
53
|
"@happyvertical/smrt-playground",
|
|
54
|
+
"@happyvertical/smrt-profiles",
|
|
52
55
|
"@happyvertical/smrt-prompts",
|
|
53
56
|
"@happyvertical/smrt-tenancy",
|
|
54
57
|
"@happyvertical/smrt-types",
|
|
@@ -107,96 +110,111 @@
|
|
|
107
110
|
"name": "name",
|
|
108
111
|
"type": "text",
|
|
109
112
|
"required": false,
|
|
110
|
-
"columnType": "TEXT"
|
|
113
|
+
"columnType": "TEXT",
|
|
114
|
+
"description": "A recognizable name for this file or media item."
|
|
111
115
|
},
|
|
112
116
|
{
|
|
113
117
|
"name": "sourceUri",
|
|
114
118
|
"type": "text",
|
|
115
119
|
"required": false,
|
|
116
|
-
"columnType": "TEXT"
|
|
120
|
+
"columnType": "TEXT",
|
|
121
|
+
"description": "Where the original file is stored or can be retrieved."
|
|
117
122
|
},
|
|
118
123
|
{
|
|
119
124
|
"name": "mimeType",
|
|
120
125
|
"type": "text",
|
|
121
126
|
"required": false,
|
|
122
|
-
"columnType": "TEXT"
|
|
127
|
+
"columnType": "TEXT",
|
|
128
|
+
"description": "The file format, such as image/jpeg or application/pdf."
|
|
123
129
|
},
|
|
124
130
|
{
|
|
125
131
|
"name": "description",
|
|
126
132
|
"type": "text",
|
|
127
133
|
"required": false,
|
|
128
|
-
"columnType": "TEXT"
|
|
134
|
+
"columnType": "TEXT",
|
|
135
|
+
"description": "Explain what the file contains and when it should be used."
|
|
129
136
|
},
|
|
130
137
|
{
|
|
131
138
|
"name": "metadata",
|
|
132
139
|
"type": "text",
|
|
133
140
|
"required": false,
|
|
134
|
-
"columnType": "TEXT"
|
|
141
|
+
"columnType": "TEXT",
|
|
142
|
+
"description": "Additional details recorded while processing this file."
|
|
135
143
|
},
|
|
136
144
|
{
|
|
137
145
|
"name": "version",
|
|
138
146
|
"type": "integer",
|
|
139
147
|
"required": true,
|
|
140
148
|
"columnType": "INTEGER",
|
|
141
|
-
"default": 1
|
|
149
|
+
"default": 1,
|
|
150
|
+
"description": "The revision number of this file in its version history."
|
|
142
151
|
},
|
|
143
152
|
{
|
|
144
153
|
"name": "primaryVersionId",
|
|
145
154
|
"type": "foreignKey",
|
|
146
155
|
"required": false,
|
|
147
156
|
"related": "Asset",
|
|
148
|
-
"columnType": "UUID"
|
|
157
|
+
"columnType": "UUID",
|
|
158
|
+
"description": "The first version of this file, linking its revision history."
|
|
149
159
|
},
|
|
150
160
|
{
|
|
151
161
|
"name": "typeSlug",
|
|
152
162
|
"type": "text",
|
|
153
163
|
"required": false,
|
|
154
|
-
"columnType": "TEXT"
|
|
164
|
+
"columnType": "TEXT",
|
|
165
|
+
"description": "The category of asset, such as an image, video, or document."
|
|
155
166
|
},
|
|
156
167
|
{
|
|
157
168
|
"name": "statusSlug",
|
|
158
169
|
"type": "text",
|
|
159
170
|
"required": false,
|
|
160
|
-
"columnType": "TEXT"
|
|
171
|
+
"columnType": "TEXT",
|
|
172
|
+
"description": "The stage of this asset, such as draft, published, or archived."
|
|
161
173
|
},
|
|
162
174
|
{
|
|
163
175
|
"name": "ownerProfileId",
|
|
164
176
|
"type": "crossPackageRef",
|
|
165
177
|
"required": false,
|
|
166
178
|
"related": "@happyvertical/smrt-profiles:Profile",
|
|
167
|
-
"columnType": "UUID"
|
|
179
|
+
"columnType": "UUID",
|
|
180
|
+
"description": "The person or organization responsible for this asset."
|
|
168
181
|
},
|
|
169
182
|
{
|
|
170
183
|
"name": "sourceAssetId",
|
|
171
184
|
"type": "foreignKey",
|
|
172
185
|
"required": false,
|
|
173
186
|
"related": "Asset",
|
|
174
|
-
"columnType": "UUID"
|
|
187
|
+
"columnType": "UUID",
|
|
188
|
+
"description": "The original asset used to produce this file or variation."
|
|
175
189
|
},
|
|
176
190
|
{
|
|
177
191
|
"name": "folderId",
|
|
178
192
|
"type": "foreignKey",
|
|
179
193
|
"required": false,
|
|
180
194
|
"related": "Folder",
|
|
181
|
-
"columnType": "UUID"
|
|
195
|
+
"columnType": "UUID",
|
|
196
|
+
"description": "The folder that organizes this file in the asset library."
|
|
182
197
|
},
|
|
183
198
|
{
|
|
184
199
|
"name": "sourceType",
|
|
185
200
|
"type": "text",
|
|
186
201
|
"required": false,
|
|
187
|
-
"columnType": "TEXT"
|
|
202
|
+
"columnType": "TEXT",
|
|
203
|
+
"description": "The service or source from which this file was obtained."
|
|
188
204
|
},
|
|
189
205
|
{
|
|
190
206
|
"name": "externalId",
|
|
191
207
|
"type": "text",
|
|
192
208
|
"required": false,
|
|
193
|
-
"columnType": "TEXT"
|
|
209
|
+
"columnType": "TEXT",
|
|
210
|
+
"description": "The reference used to identify this file at its original source."
|
|
194
211
|
},
|
|
195
212
|
{
|
|
196
213
|
"name": "externalRefs",
|
|
197
214
|
"type": "text",
|
|
198
215
|
"required": false,
|
|
199
|
-
"columnType": "TEXT"
|
|
216
|
+
"columnType": "TEXT",
|
|
217
|
+
"description": "References that connect this file to copies held by other services."
|
|
200
218
|
},
|
|
201
219
|
{
|
|
202
220
|
"name": "createdAt",
|