@glassly/miniapp 0.3.1-beta.536 → 0.3.1-beta.561

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.
@@ -8,7 +8,7 @@
8
8
  * miniapp that wraps text with them gets the rows the glasses will draw.
9
9
  */
10
10
  export type { DisplayProfile, FontMetrics, UniformScriptWidths, FallbackConfig, DisplayConstraints, ScriptType, } from "./profiles";
11
- export { G1_PROFILE, G1_PROFILE_LEGACY, G1_HYPHEN_WIDTH_PX, G1_SPACE_WIDTH_PX, G2_PROFILE, G2_HYPHEN_WIDTH_PX, G2_SPACE_WIDTH_PX, Z100_PROFILE, Z100_HYPHEN_WIDTH_PX, Z100_SPACE_WIDTH_PX, NEX_PROFILE, NEX_HYPHEN_WIDTH_PX, NEX_SPACE_WIDTH_PX, } from "./profiles";
11
+ export { G1_PROFILE, G1_PROFILE_LEGACY, G1_HYPHEN_WIDTH_PX, G1_SPACE_WIDTH_PX, G2_PROFILE, G2_HYPHEN_WIDTH_PX, G2_SPACE_WIDTH_PX, Z100_PROFILE, Z100_HYPHEN_WIDTH_PX, Z100_SPACE_WIDTH_PX, NEX_PROFILE, NEX_HYPHEN_WIDTH_PX, NEX_SPACE_WIDTH_PX, RAYNEO_IO_PROFILE, RAYNEO_IO_HYPHEN_WIDTH_PX, RAYNEO_IO_SPACE_WIDTH_PX, } from "./profiles";
12
12
  export { TextMeasurer } from "./measurer";
13
13
  export type { CharMeasurement, TextMeasurement } from "./measurer";
14
14
  export { detectScript, isCJKCharacter, isKoreanCharacter, isUniformWidthScript, isUnsupportedScript, needsHyphenForBreak, SCRIPT_RANGES, } from "./measurer";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/display/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EACV,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,UAAU,GACX,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAC,YAAY,EAAC,MAAM,YAAY,CAAA;AACvC,YAAY,EAAC,eAAe,EAAE,eAAe,EAAC,MAAM,YAAY,CAAA;AAChE,OAAO,EACL,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,GACd,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAC,WAAW,EAAE,oBAAoB,EAAC,MAAM,WAAW,CAAA;AAC3D,YAAY,EAAC,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAC,MAAM,WAAW,CAAA;AAE9E,OAAO,EAAC,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAC,MAAM,YAAY,CAAA;AACjF,YAAY,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAA;AAExC,YAAY,EACV,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,aAAa,EACb,cAAc,EACd,WAAW,EACX,wBAAwB,EACxB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,GAChB,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAC,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,EAAC,MAAM,oBAAoB,CAAA;AACnG,YAAY,EAAC,kBAAkB,EAAE,aAAa,EAAC,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/display/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EACV,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,UAAU,GACX,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAC,YAAY,EAAC,MAAM,YAAY,CAAA;AACvC,YAAY,EAAC,eAAe,EAAE,eAAe,EAAC,MAAM,YAAY,CAAA;AAChE,OAAO,EACL,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,GACd,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAC,WAAW,EAAE,oBAAoB,EAAC,MAAM,WAAW,CAAA;AAC3D,YAAY,EAAC,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAC,MAAM,WAAW,CAAA;AAE9E,OAAO,EAAC,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAC,MAAM,YAAY,CAAA;AACjF,YAAY,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAA;AAExC,YAAY,EACV,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,aAAa,EACb,cAAc,EACd,WAAW,EACX,wBAAwB,EACxB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,GAChB,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAC,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,EAAC,MAAM,oBAAoB,CAAA;AACnG,YAAY,EAAC,kBAAkB,EAAE,aAAa,EAAC,MAAM,oBAAoB,CAAA"}
@@ -7,7 +7,7 @@
7
7
  * the engine's scene pipeline (`processScene`) uses these same modules, so a
8
8
  * miniapp that wraps text with them gets the rows the glasses will draw.
9
9
  */
10
- export { G1_PROFILE, G1_PROFILE_LEGACY, G1_HYPHEN_WIDTH_PX, G1_SPACE_WIDTH_PX, G2_PROFILE, G2_HYPHEN_WIDTH_PX, G2_SPACE_WIDTH_PX, Z100_PROFILE, Z100_HYPHEN_WIDTH_PX, Z100_SPACE_WIDTH_PX, NEX_PROFILE, NEX_HYPHEN_WIDTH_PX, NEX_SPACE_WIDTH_PX, } from "./profiles";
10
+ export { G1_PROFILE, G1_PROFILE_LEGACY, G1_HYPHEN_WIDTH_PX, G1_SPACE_WIDTH_PX, G2_PROFILE, G2_HYPHEN_WIDTH_PX, G2_SPACE_WIDTH_PX, Z100_PROFILE, Z100_HYPHEN_WIDTH_PX, Z100_SPACE_WIDTH_PX, NEX_PROFILE, NEX_HYPHEN_WIDTH_PX, NEX_SPACE_WIDTH_PX, RAYNEO_IO_PROFILE, RAYNEO_IO_HYPHEN_WIDTH_PX, RAYNEO_IO_SPACE_WIDTH_PX, } from "./profiles";
11
11
  export { TextMeasurer } from "./measurer";
12
12
  export { detectScript, isCJKCharacter, isKoreanCharacter, isUniformWidthScript, isUnsupportedScript, needsHyphenForBreak, SCRIPT_RANGES, } from "./measurer";
13
13
  export { TextWrapper, DEFAULT_WRAP_OPTIONS } from "./wrapper";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/display/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAUH,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAC,YAAY,EAAC,MAAM,YAAY,CAAA;AAEvC,OAAO,EACL,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,GACd,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAC,WAAW,EAAE,oBAAoB,EAAC,MAAM,WAAW,CAAA;AAG3D,OAAO,EAAC,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAC,MAAM,YAAY,CAAA;AAuBjF,OAAO,EACL,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAC,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,EAAC,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/display/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAUH,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAC,YAAY,EAAC,MAAM,YAAY,CAAA;AAEvC,OAAO,EACL,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,GACd,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAC,WAAW,EAAE,oBAAoB,EAAC,MAAM,WAAW,CAAA;AAG3D,OAAO,EAAC,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAC,MAAM,YAAY,CAAA;AAuBjF,OAAO,EACL,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAC,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,EAAC,MAAM,oBAAoB,CAAA"}
@@ -9,4 +9,5 @@ export { G1_PROFILE, G1_PROFILE_LEGACY, G1_HYPHEN_WIDTH_PX, G1_SPACE_WIDTH_PX }
9
9
  export { Z100_PROFILE, Z100_HYPHEN_WIDTH_PX, Z100_SPACE_WIDTH_PX } from "./z100";
10
10
  export { G2_PROFILE, G2_HYPHEN_WIDTH_PX, G2_SPACE_WIDTH_PX } from "./g2";
11
11
  export { NEX_PROFILE, NEX_HYPHEN_WIDTH_PX, NEX_SPACE_WIDTH_PX } from "./nex";
12
+ export { RAYNEO_IO_PROFILE, RAYNEO_IO_HYPHEN_WIDTH_PX, RAYNEO_IO_SPACE_WIDTH_PX } from "./rayneo-io";
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/display/profiles/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,YAAY,EACV,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,UAAU,GACX,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAC,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAA;AAGzF,OAAO,EAAC,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAC,MAAM,QAAQ,CAAA;AAG9E,OAAO,EAAC,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAA;AAGtE,OAAO,EAAC,WAAW,EAAE,mBAAmB,EAAE,kBAAkB,EAAC,MAAM,OAAO,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/display/profiles/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,YAAY,EACV,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,UAAU,GACX,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAC,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAA;AAGzF,OAAO,EAAC,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAC,MAAM,QAAQ,CAAA;AAG9E,OAAO,EAAC,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAA;AAGtE,OAAO,EAAC,WAAW,EAAE,mBAAmB,EAAE,kBAAkB,EAAC,MAAM,OAAO,CAAA;AAG1E,OAAO,EAAC,iBAAiB,EAAE,yBAAyB,EAAE,wBAAwB,EAAC,MAAM,aAAa,CAAA"}
@@ -12,4 +12,6 @@ export { Z100_PROFILE, Z100_HYPHEN_WIDTH_PX, Z100_SPACE_WIDTH_PX } from "./z100"
12
12
  export { G2_PROFILE, G2_HYPHEN_WIDTH_PX, G2_SPACE_WIDTH_PX } from "./g2";
13
13
  // Mentra Nex Profile (a.k.a. Mentra Display)
14
14
  export { NEX_PROFILE, NEX_HYPHEN_WIDTH_PX, NEX_SPACE_WIDTH_PX } from "./nex";
15
+ // RayNeo iO Profile (stock subtitle page)
16
+ export { RAYNEO_IO_PROFILE, RAYNEO_IO_HYPHEN_WIDTH_PX, RAYNEO_IO_SPACE_WIDTH_PX } from "./rayneo-io";
15
17
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/display/profiles/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,cAAc;AACd,OAAO,EAAC,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAA;AAEzF,eAAe;AACf,OAAO,EAAC,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAC,MAAM,QAAQ,CAAA;AAE9E,aAAa;AACb,OAAO,EAAC,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAA;AAEtE,6CAA6C;AAC7C,OAAO,EAAC,WAAW,EAAE,mBAAmB,EAAE,kBAAkB,EAAC,MAAM,OAAO,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/display/profiles/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,cAAc;AACd,OAAO,EAAC,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAA;AAEzF,eAAe;AACf,OAAO,EAAC,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAC,MAAM,QAAQ,CAAA;AAE9E,aAAa;AACb,OAAO,EAAC,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,MAAM,CAAA;AAEtE,6CAA6C;AAC7C,OAAO,EAAC,WAAW,EAAE,mBAAmB,EAAE,kBAAkB,EAAC,MAAM,OAAO,CAAA;AAE1E,0CAA0C;AAC1C,OAAO,EAAC,iBAAiB,EAAE,yBAAyB,EAAE,wBAAwB,EAAC,MAAM,aAAa,CAAA"}
@@ -0,0 +1,30 @@
1
+ import { DisplayProfile } from "./types";
2
+ /**
3
+ * RayNeo iO Smart Glasses Display Profile (stock firmware text path)
4
+ *
5
+ * On stock firmware the glasses show text through their own subtitle page,
6
+ * which accepts at most 96 characters per update and lays the text out
7
+ * itself. The profile keeps the phone-side wrapping inside what that page
8
+ * can show at once.
9
+ *
10
+ * Display specs:
11
+ * - Panel: 540×180, 1-bit monochrome
12
+ * - Usable text width: ~240px at the subtitle page's font size 2
13
+ * - Max text lines: 4 (the page allows 5; one is kept in reserve)
14
+ * - Max payload: 96 characters per subtitle update
15
+ *
16
+ * With the Glassly CFW the engine renders scene frames onto the 540×180
17
+ * page instead and wraps against that canvas.
18
+ */
19
+ export declare const RAYNEO_IO_PROFILE: DisplayProfile;
20
+ /**
21
+ * Get the hyphen width for RayNeo iO in pixels.
22
+ * Used for line breaking calculations.
23
+ */
24
+ export declare const RAYNEO_IO_HYPHEN_WIDTH_PX = 6;
25
+ /**
26
+ * Get the space width for RayNeo iO in pixels.
27
+ * Used for column alignment calculations.
28
+ */
29
+ export declare const RAYNEO_IO_SPACE_WIDTH_PX = 5;
30
+ //# sourceMappingURL=rayneo-io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rayneo-io.d.ts","sourceRoot":"","sources":["../../../src/display/profiles/rayneo-io.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,SAAS,CAAA;AA0HtC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAyC/B,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAA;AAE1C;;;GAGG;AACH,eAAO,MAAM,wBAAwB,IAAI,CAAA"}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * RayNeo iO glyph widths - approximate metrics of the stock subtitle page font.
3
+ *
4
+ * The stock firmware renders subtitle text itself (business 19, font_size 2)
5
+ * with a near-uniform sans face; these widths are estimates that keep the
6
+ * phone-side wrapper slightly conservative so a line never overflows on the
7
+ * glasses. On the Glassly CFW the engine wraps against the 540×180 scene
8
+ * canvas instead and this profile is not used.
9
+ */
10
+ const RAYNEO_IO_GLYPH_WIDTHS = {
11
+ // Space and punctuation
12
+ " ": 5,
13
+ "!": 5,
14
+ '"': 7,
15
+ "#": 10,
16
+ "$": 10,
17
+ "%": 14,
18
+ "&": 12,
19
+ "'": 4,
20
+ "(": 6,
21
+ ")": 6,
22
+ "*": 8,
23
+ "+": 10,
24
+ ",": 5,
25
+ "-": 6, // Hyphen width - important for line breaking
26
+ ".": 5,
27
+ "/": 7,
28
+ // Numbers
29
+ "0": 10,
30
+ "1": 10,
31
+ "2": 10,
32
+ "3": 10,
33
+ "4": 10,
34
+ "5": 10,
35
+ "6": 10,
36
+ "7": 10,
37
+ "8": 10,
38
+ "9": 10,
39
+ // More punctuation
40
+ ":": 5,
41
+ ";": 5,
42
+ "<": 10,
43
+ "=": 10,
44
+ ">": 10,
45
+ "?": 8,
46
+ "@": 16,
47
+ // Uppercase
48
+ "A": 12,
49
+ "B": 11,
50
+ "C": 11,
51
+ "D": 12,
52
+ "E": 10,
53
+ "F": 9,
54
+ "G": 12,
55
+ "H": 12,
56
+ "I": 5,
57
+ "J": 7,
58
+ "K": 11,
59
+ "L": 9,
60
+ "M": 15,
61
+ "N": 12,
62
+ "O": 13,
63
+ "P": 11,
64
+ "Q": 13,
65
+ "R": 11,
66
+ "S": 10,
67
+ "T": 10,
68
+ "U": 12,
69
+ "V": 12,
70
+ "W": 16,
71
+ "X": 11,
72
+ "Y": 11,
73
+ "Z": 10,
74
+ // Brackets & special
75
+ "[": 6,
76
+ "\\": 7,
77
+ "]": 6,
78
+ "^": 10,
79
+ "_": 9,
80
+ "`": 5,
81
+ // Lowercase
82
+ "a": 10,
83
+ "b": 10,
84
+ "c": 9,
85
+ "d": 10,
86
+ "e": 10,
87
+ "f": 6,
88
+ "g": 10,
89
+ "h": 10,
90
+ "i": 4,
91
+ "j": 4,
92
+ "k": 9,
93
+ "l": 4,
94
+ "m": 15,
95
+ "n": 10,
96
+ "o": 10,
97
+ "p": 10,
98
+ "q": 10,
99
+ "r": 7,
100
+ "s": 8,
101
+ "t": 6,
102
+ "u": 10,
103
+ "v": 9,
104
+ "w": 14,
105
+ "x": 9,
106
+ "y": 9,
107
+ "z": 8,
108
+ // More special
109
+ "{": 6,
110
+ "|": 5,
111
+ "}": 6,
112
+ "~": 10,
113
+ };
114
+ /**
115
+ * RayNeo iO Smart Glasses Display Profile (stock firmware text path)
116
+ *
117
+ * On stock firmware the glasses show text through their own subtitle page,
118
+ * which accepts at most 96 characters per update and lays the text out
119
+ * itself. The profile keeps the phone-side wrapping inside what that page
120
+ * can show at once.
121
+ *
122
+ * Display specs:
123
+ * - Panel: 540×180, 1-bit monochrome
124
+ * - Usable text width: ~240px at the subtitle page's font size 2
125
+ * - Max text lines: 4 (the page allows 5; one is kept in reserve)
126
+ * - Max payload: 96 characters per subtitle update
127
+ *
128
+ * With the Glassly CFW the engine renders scene frames onto the 540×180
129
+ * page instead and wraps against that canvas.
130
+ */
131
+ export const RAYNEO_IO_PROFILE = {
132
+ id: "rayneo-io",
133
+ name: "RayNeo iO",
134
+ // Display dimensions
135
+ displayWidthPx: 240,
136
+ maxLines: 4,
137
+ // BLE constraints
138
+ // The subtitle page takes at most 96 characters per update; frames larger
139
+ // than one ATT write are split by the SGC.
140
+ maxPayloadBytes: 96,
141
+ bleChunkSize: 180,
142
+ // Font metrics - near-uniform sans face at the subtitle page's font size 2
143
+ fontMetrics: {
144
+ glyphWidths: new Map(Object.entries(RAYNEO_IO_GLYPH_WIDTHS)),
145
+ defaultGlyphWidth: 10,
146
+ // Direct pixel widths, no render formula needed
147
+ renderFormula: (glyphWidth) => glyphWidth,
148
+ // Uniform-width scripts for CJK and other non-Latin
149
+ uniformScripts: {
150
+ cjk: 18,
151
+ hiragana: 18,
152
+ katakana: 18,
153
+ korean: 18,
154
+ cyrillic: 11,
155
+ },
156
+ fallback: {
157
+ latinMaxWidth: 16, // 'W' and '@' are the widest at 16px
158
+ unknownBehavior: "useLatinMax",
159
+ },
160
+ },
161
+ constraints: {
162
+ minCharsBeforeHyphen: 3,
163
+ noStartChars: [".", ",", "!", "?", ":", ";", ")", "]", "}", "。", ",", "!", "?", ":", ";", ")", "】", "」"],
164
+ noEndChars: ["(", "[", "{", "(", "【", "「"],
165
+ },
166
+ };
167
+ /**
168
+ * Get the hyphen width for RayNeo iO in pixels.
169
+ * Used for line breaking calculations.
170
+ */
171
+ export const RAYNEO_IO_HYPHEN_WIDTH_PX = 6;
172
+ /**
173
+ * Get the space width for RayNeo iO in pixels.
174
+ * Used for column alignment calculations.
175
+ */
176
+ export const RAYNEO_IO_SPACE_WIDTH_PX = 5;
177
+ //# sourceMappingURL=rayneo-io.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rayneo-io.js","sourceRoot":"","sources":["../../../src/display/profiles/rayneo-io.ts"],"names":[],"mappings":"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"}
@@ -1,15 +1,8 @@
1
- /**
2
- * The vocabulary of `miniapp.json`: what a manifest may declare under
3
- * `permissions` and `hardwareRequirements`. The engine types its manifest
4
- * against these and the CLI validates with them, so an entry added here
5
- * reaches both without a hand-copied list.
6
- */
7
- import { HardwareRequirementLevel, HardwareType } from "./hardware";
8
1
  export declare const MANIFEST_PERMISSION_TYPES: readonly ["MICROPHONE", "CAMERA", "CALENDAR", "LOCATION", "BACKGROUND_LOCATION", "READ_NOTIFICATIONS", "DISPLAY_MIRROR", "POST_NOTIFICATIONS", "API_KEYS", "MEMORY", "AI", "SPEAKERS", "MANAGE_MINIAPPS", "PLATFORM_SETTINGS"];
9
2
  export type ManifestPermissionType = (typeof MANIFEST_PERMISSION_TYPES)[number];
10
3
  /** Every hardware type an author may declare. EXIST is implied by the host. */
11
- export declare const MANIFEST_HARDWARE_TYPES: readonly [HardwareType.CAMERA, HardwareType.DISPLAY, HardwareType.MICROPHONE, HardwareType.SPEAKER, HardwareType.IMU, HardwareType.BUTTON, HardwareType.LIGHT, HardwareType.WIFI];
4
+ export declare const MANIFEST_HARDWARE_TYPES: readonly ["CAMERA", "DISPLAY", "MICROPHONE", "SPEAKER", "IMU", "BUTTON", "LIGHT", "WIFI"];
12
5
  export type ManifestHardwareType = (typeof MANIFEST_HARDWARE_TYPES)[number];
13
- export declare const MANIFEST_HARDWARE_LEVELS: readonly [HardwareRequirementLevel.REQUIRED, HardwareRequirementLevel.OPTIONAL];
6
+ export declare const MANIFEST_HARDWARE_LEVELS: readonly ["REQUIRED", "OPTIONAL"];
14
7
  export type ManifestHardwareLevel = (typeof MANIFEST_HARDWARE_LEVELS)[number];
15
8
  //# sourceMappingURL=manifest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAC,wBAAwB,EAAE,YAAY,EAAC,MAAM,YAAY,CAAA;AAEjE,eAAO,MAAM,yBAAyB,gOAe5B,CAAA;AAEV,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE/E,+EAA+E;AAC/E,eAAO,MAAM,uBAAuB,mLAS1B,CAAA;AAEV,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE3E,eAAO,MAAM,wBAAwB,iFAAkF,CAAA;AAEvH,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAA"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,yBAAyB,gOAe5B,CAAA;AAEV,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE/E,+EAA+E;AAC/E,eAAO,MAAM,uBAAuB,2FAA4F,CAAA;AAEhI,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE3E,eAAO,MAAM,wBAAwB,mCAAoC,CAAA;AAEzE,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAA"}
package/dist/manifest.js CHANGED
@@ -1,10 +1,3 @@
1
- /**
2
- * The vocabulary of `miniapp.json`: what a manifest may declare under
3
- * `permissions` and `hardwareRequirements`. The engine types its manifest
4
- * against these and the CLI validates with them, so an entry added here
5
- * reaches both without a hand-copied list.
6
- */
7
- import { HardwareRequirementLevel, HardwareType } from "./hardware";
8
1
  export const MANIFEST_PERMISSION_TYPES = [
9
2
  "MICROPHONE",
10
3
  "CAMERA",
@@ -22,15 +15,10 @@ export const MANIFEST_PERMISSION_TYPES = [
22
15
  "PLATFORM_SETTINGS",
23
16
  ];
24
17
  /** Every hardware type an author may declare. EXIST is implied by the host. */
25
- export const MANIFEST_HARDWARE_TYPES = [
26
- HardwareType.CAMERA,
27
- HardwareType.DISPLAY,
28
- HardwareType.MICROPHONE,
29
- HardwareType.SPEAKER,
30
- HardwareType.IMU,
31
- HardwareType.BUTTON,
32
- HardwareType.LIGHT,
33
- HardwareType.WIFI,
34
- ];
35
- export const MANIFEST_HARDWARE_LEVELS = [HardwareRequirementLevel.REQUIRED, HardwareRequirementLevel.OPTIONAL];
18
+ export const MANIFEST_HARDWARE_TYPES = ["CAMERA", "DISPLAY", "MICROPHONE", "SPEAKER", "IMU", "BUTTON", "LIGHT", "WIFI"];
19
+ export const MANIFEST_HARDWARE_LEVELS = ["REQUIRED", "OPTIONAL"];
20
+ const hardwareTypesMatchEnum = true;
21
+ const hardwareLevelsMatchEnum = true;
22
+ void hardwareTypesMatchEnum;
23
+ void hardwareLevelsMatchEnum;
36
24
  //# sourceMappingURL=manifest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAC,wBAAwB,EAAE,YAAY,EAAC,MAAM,YAAY,CAAA;AAEjE,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,YAAY;IACZ,QAAQ;IACR,UAAU;IACV,UAAU;IACV,qBAAqB;IACrB,oBAAoB;IACpB,gBAAgB;IAChB,oBAAoB;IACpB,UAAU;IACV,QAAQ;IACR,IAAI;IACJ,UAAU;IACV,iBAAiB;IACjB,mBAAmB;CACX,CAAA;AAIV,+EAA+E;AAC/E,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,YAAY,CAAC,MAAM;IACnB,YAAY,CAAC,OAAO;IACpB,YAAY,CAAC,UAAU;IACvB,YAAY,CAAC,OAAO;IACpB,YAAY,CAAC,GAAG;IAChB,YAAY,CAAC,MAAM;IACnB,YAAY,CAAC,KAAK;IAClB,YAAY,CAAC,IAAI;CACT,CAAA;AAIV,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,wBAAwB,CAAC,QAAQ,EAAE,wBAAwB,CAAC,QAAQ,CAAU,CAAA"}
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,YAAY;IACZ,QAAQ;IACR,UAAU;IACV,UAAU;IACV,qBAAqB;IACrB,oBAAoB;IACpB,gBAAgB;IAChB,oBAAoB;IACpB,UAAU;IACV,QAAQ;IACR,IAAI;IACJ,UAAU;IACV,iBAAiB;IACjB,mBAAmB;CACX,CAAA;AAIV,+EAA+E;AAC/E,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAU,CAAA;AAIhI,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,UAAU,EAAE,UAAU,CAAU,CAAA;AAOzE,MAAM,sBAAsB,GAAqE,IAAI,CAAA;AACrG,MAAM,uBAAuB,GAAgE,IAAI,CAAA;AACjG,KAAK,sBAAsB,CAAA;AAC3B,KAAK,uBAAuB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassly/miniapp",
3
- "version": "0.3.1-beta.536",
3
+ "version": "0.3.1-beta.561",
4
4
  "description": "SDK for building Glassly local miniapps (browser/WebView)",
5
5
  "author": "Glassly Labs",
6
6
  "license": "MIT",
@@ -73,7 +73,7 @@
73
73
  }
74
74
  },
75
75
  "dependencies": {
76
- "@glassly/cloud-protocol": "0.3.1-beta.536",
76
+ "@glassly/cloud-protocol": "0.3.1-beta.561",
77
77
  "eventemitter3": "^5.0.1"
78
78
  },
79
79
  "devDependencies": {
@@ -30,6 +30,9 @@ export {
30
30
  NEX_PROFILE,
31
31
  NEX_HYPHEN_WIDTH_PX,
32
32
  NEX_SPACE_WIDTH_PX,
33
+ RAYNEO_IO_PROFILE,
34
+ RAYNEO_IO_HYPHEN_WIDTH_PX,
35
+ RAYNEO_IO_SPACE_WIDTH_PX,
33
36
  } from "./profiles"
34
37
 
35
38
  export {TextMeasurer} from "./measurer"
@@ -26,3 +26,6 @@ export {G2_PROFILE, G2_HYPHEN_WIDTH_PX, G2_SPACE_WIDTH_PX} from "./g2"
26
26
 
27
27
  // Mentra Nex Profile (a.k.a. Mentra Display)
28
28
  export {NEX_PROFILE, NEX_HYPHEN_WIDTH_PX, NEX_SPACE_WIDTH_PX} from "./nex"
29
+
30
+ // RayNeo iO Profile (stock subtitle page)
31
+ export {RAYNEO_IO_PROFILE, RAYNEO_IO_HYPHEN_WIDTH_PX, RAYNEO_IO_SPACE_WIDTH_PX} from "./rayneo-io"
@@ -0,0 +1,193 @@
1
+ import {DisplayProfile} from "./types"
2
+
3
+ /**
4
+ * RayNeo iO glyph widths - approximate metrics of the stock subtitle page font.
5
+ *
6
+ * The stock firmware renders subtitle text itself (business 19, font_size 2)
7
+ * with a near-uniform sans face; these widths are estimates that keep the
8
+ * phone-side wrapper slightly conservative so a line never overflows on the
9
+ * glasses. On the Glassly CFW the engine wraps against the 540×180 scene
10
+ * canvas instead and this profile is not used.
11
+ */
12
+ const RAYNEO_IO_GLYPH_WIDTHS: Record<string, number> = {
13
+ // Space and punctuation
14
+ " ": 5,
15
+ "!": 5,
16
+ '"': 7,
17
+ "#": 10,
18
+ "$": 10,
19
+ "%": 14,
20
+ "&": 12,
21
+ "'": 4,
22
+ "(": 6,
23
+ ")": 6,
24
+ "*": 8,
25
+ "+": 10,
26
+ ",": 5,
27
+ "-": 6, // Hyphen width - important for line breaking
28
+ ".": 5,
29
+ "/": 7,
30
+
31
+ // Numbers
32
+ "0": 10,
33
+ "1": 10,
34
+ "2": 10,
35
+ "3": 10,
36
+ "4": 10,
37
+ "5": 10,
38
+ "6": 10,
39
+ "7": 10,
40
+ "8": 10,
41
+ "9": 10,
42
+
43
+ // More punctuation
44
+ ":": 5,
45
+ ";": 5,
46
+ "<": 10,
47
+ "=": 10,
48
+ ">": 10,
49
+ "?": 8,
50
+ "@": 16,
51
+
52
+ // Uppercase
53
+ "A": 12,
54
+ "B": 11,
55
+ "C": 11,
56
+ "D": 12,
57
+ "E": 10,
58
+ "F": 9,
59
+ "G": 12,
60
+ "H": 12,
61
+ "I": 5,
62
+ "J": 7,
63
+ "K": 11,
64
+ "L": 9,
65
+ "M": 15,
66
+ "N": 12,
67
+ "O": 13,
68
+ "P": 11,
69
+ "Q": 13,
70
+ "R": 11,
71
+ "S": 10,
72
+ "T": 10,
73
+ "U": 12,
74
+ "V": 12,
75
+ "W": 16,
76
+ "X": 11,
77
+ "Y": 11,
78
+ "Z": 10,
79
+
80
+ // Brackets & special
81
+ "[": 6,
82
+ "\\": 7,
83
+ "]": 6,
84
+ "^": 10,
85
+ "_": 9,
86
+ "`": 5,
87
+
88
+ // Lowercase
89
+ "a": 10,
90
+ "b": 10,
91
+ "c": 9,
92
+ "d": 10,
93
+ "e": 10,
94
+ "f": 6,
95
+ "g": 10,
96
+ "h": 10,
97
+ "i": 4,
98
+ "j": 4,
99
+ "k": 9,
100
+ "l": 4,
101
+ "m": 15,
102
+ "n": 10,
103
+ "o": 10,
104
+ "p": 10,
105
+ "q": 10,
106
+ "r": 7,
107
+ "s": 8,
108
+ "t": 6,
109
+ "u": 10,
110
+ "v": 9,
111
+ "w": 14,
112
+ "x": 9,
113
+ "y": 9,
114
+ "z": 8,
115
+
116
+ // More special
117
+ "{": 6,
118
+ "|": 5,
119
+ "}": 6,
120
+ "~": 10,
121
+ }
122
+
123
+ /**
124
+ * RayNeo iO Smart Glasses Display Profile (stock firmware text path)
125
+ *
126
+ * On stock firmware the glasses show text through their own subtitle page,
127
+ * which accepts at most 96 characters per update and lays the text out
128
+ * itself. The profile keeps the phone-side wrapping inside what that page
129
+ * can show at once.
130
+ *
131
+ * Display specs:
132
+ * - Panel: 540×180, 1-bit monochrome
133
+ * - Usable text width: ~240px at the subtitle page's font size 2
134
+ * - Max text lines: 4 (the page allows 5; one is kept in reserve)
135
+ * - Max payload: 96 characters per subtitle update
136
+ *
137
+ * With the Glassly CFW the engine renders scene frames onto the 540×180
138
+ * page instead and wraps against that canvas.
139
+ */
140
+ export const RAYNEO_IO_PROFILE: DisplayProfile = {
141
+ id: "rayneo-io",
142
+ name: "RayNeo iO",
143
+
144
+ // Display dimensions
145
+ displayWidthPx: 240,
146
+ maxLines: 4,
147
+
148
+ // BLE constraints
149
+ // The subtitle page takes at most 96 characters per update; frames larger
150
+ // than one ATT write are split by the SGC.
151
+ maxPayloadBytes: 96,
152
+ bleChunkSize: 180,
153
+
154
+ // Font metrics - near-uniform sans face at the subtitle page's font size 2
155
+ fontMetrics: {
156
+ glyphWidths: new Map(Object.entries(RAYNEO_IO_GLYPH_WIDTHS)),
157
+ defaultGlyphWidth: 10,
158
+ // Direct pixel widths, no render formula needed
159
+ renderFormula: (glyphWidth: number) => glyphWidth,
160
+
161
+ // Uniform-width scripts for CJK and other non-Latin
162
+ uniformScripts: {
163
+ cjk: 18,
164
+ hiragana: 18,
165
+ katakana: 18,
166
+ korean: 18,
167
+ cyrillic: 11,
168
+ },
169
+
170
+ fallback: {
171
+ latinMaxWidth: 16, // 'W' and '@' are the widest at 16px
172
+ unknownBehavior: "useLatinMax",
173
+ },
174
+ },
175
+
176
+ constraints: {
177
+ minCharsBeforeHyphen: 3,
178
+ noStartChars: [".", ",", "!", "?", ":", ";", ")", "]", "}", "。", ",", "!", "?", ":", ";", ")", "】", "」"],
179
+ noEndChars: ["(", "[", "{", "(", "【", "「"],
180
+ },
181
+ }
182
+
183
+ /**
184
+ * Get the hyphen width for RayNeo iO in pixels.
185
+ * Used for line breaking calculations.
186
+ */
187
+ export const RAYNEO_IO_HYPHEN_WIDTH_PX = 6
188
+
189
+ /**
190
+ * Get the space width for RayNeo iO in pixels.
191
+ * Used for column alignment calculations.
192
+ */
193
+ export const RAYNEO_IO_SPACE_WIDTH_PX = 5
package/src/manifest.ts CHANGED
@@ -3,8 +3,13 @@
3
3
  * `permissions` and `hardwareRequirements`. The engine types its manifest
4
4
  * against these and the CLI validates with them, so an entry added here
5
5
  * reaches both without a hand-copied list.
6
+ *
7
+ * Plain string literals, not enum members: manifests are JSON, and the CLI
8
+ * compares against what authors typed. The assertions at the bottom keep the
9
+ * hardware lists equal to the `HardwareType` and `HardwareRequirementLevel`
10
+ * enums at compile time.
6
11
  */
7
- import {HardwareRequirementLevel, HardwareType} from "./hardware"
12
+ import type {HardwareRequirementLevel, HardwareType} from "./hardware"
8
13
 
9
14
  export const MANIFEST_PERMISSION_TYPES = [
10
15
  "MICROPHONE",
@@ -26,19 +31,18 @@ export const MANIFEST_PERMISSION_TYPES = [
26
31
  export type ManifestPermissionType = (typeof MANIFEST_PERMISSION_TYPES)[number]
27
32
 
28
33
  /** Every hardware type an author may declare. EXIST is implied by the host. */
29
- export const MANIFEST_HARDWARE_TYPES = [
30
- HardwareType.CAMERA,
31
- HardwareType.DISPLAY,
32
- HardwareType.MICROPHONE,
33
- HardwareType.SPEAKER,
34
- HardwareType.IMU,
35
- HardwareType.BUTTON,
36
- HardwareType.LIGHT,
37
- HardwareType.WIFI,
38
- ] as const
34
+ export const MANIFEST_HARDWARE_TYPES = ["CAMERA", "DISPLAY", "MICROPHONE", "SPEAKER", "IMU", "BUTTON", "LIGHT", "WIFI"] as const
39
35
 
40
36
  export type ManifestHardwareType = (typeof MANIFEST_HARDWARE_TYPES)[number]
41
37
 
42
- export const MANIFEST_HARDWARE_LEVELS = [HardwareRequirementLevel.REQUIRED, HardwareRequirementLevel.OPTIONAL] as const
38
+ export const MANIFEST_HARDWARE_LEVELS = ["REQUIRED", "OPTIONAL"] as const
43
39
 
44
40
  export type ManifestHardwareLevel = (typeof MANIFEST_HARDWARE_LEVELS)[number]
41
+
42
+ // Compile-time equality with the enums (string enums widen to their values in
43
+ // a template literal type). A mismatch in either direction fails the build.
44
+ type Equal<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never
45
+ const hardwareTypesMatchEnum: Equal<ManifestHardwareType, Exclude<`${HardwareType}`, "EXIST">> = true
46
+ const hardwareLevelsMatchEnum: Equal<ManifestHardwareLevel, `${HardwareRequirementLevel}`> = true
47
+ void hardwareTypesMatchEnum
48
+ void hardwareLevelsMatchEnum