busybar-kit 1.1.0 → 2.1.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.
Files changed (50) hide show
  1. package/dist/config-spec.d.ts +63 -19
  2. package/dist/config-spec.d.ts.map +1 -1
  3. package/dist/config-spec.js +64 -41
  4. package/dist/config-spec.js.map +1 -1
  5. package/dist/image/index.d.ts +5 -2
  6. package/dist/image/index.d.ts.map +1 -1
  7. package/dist/image/index.js +5 -2
  8. package/dist/image/index.js.map +1 -1
  9. package/dist/image/png.d.ts +9 -0
  10. package/dist/image/png.d.ts.map +1 -0
  11. package/dist/image/png.js +165 -0
  12. package/dist/image/png.js.map +1 -0
  13. package/dist/image/resize.d.ts +12 -0
  14. package/dist/image/resize.d.ts.map +1 -0
  15. package/dist/image/resize.js +83 -0
  16. package/dist/image/resize.js.map +1 -0
  17. package/dist/index.d.ts +3 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +3 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/input/index.d.ts +3 -0
  22. package/dist/input/index.d.ts.map +1 -0
  23. package/dist/input/index.js +3 -0
  24. package/dist/input/index.js.map +1 -0
  25. package/dist/input/input.d.ts +54 -0
  26. package/dist/input/input.d.ts.map +1 -0
  27. package/dist/input/input.js +222 -0
  28. package/dist/input/input.js.map +1 -0
  29. package/dist/input/protobuf.d.ts +28 -0
  30. package/dist/input/protobuf.d.ts.map +1 -0
  31. package/dist/input/protobuf.js +81 -0
  32. package/dist/input/protobuf.js.map +1 -0
  33. package/dist/preview/png.d.ts.map +1 -1
  34. package/dist/preview/png.js +20 -4
  35. package/dist/preview/png.js.map +1 -1
  36. package/dist/preview/raster.d.ts +8 -0
  37. package/dist/preview/raster.d.ts.map +1 -1
  38. package/dist/preview/raster.js +55 -7
  39. package/dist/preview/raster.js.map +1 -1
  40. package/dist/rules.d.ts +7 -0
  41. package/dist/rules.d.ts.map +1 -0
  42. package/dist/rules.js +71 -0
  43. package/dist/rules.js.map +1 -0
  44. package/dist/summary.d.ts +18 -0
  45. package/dist/summary.d.ts.map +1 -0
  46. package/dist/summary.js +63 -0
  47. package/dist/summary.js.map +1 -0
  48. package/package.json +6 -2
  49. package/scripts/emit-config-spec.mjs +31 -0
  50. package/scripts/fix-esm-extensions.mjs +1 -1
@@ -1,16 +1,20 @@
1
1
  /**
2
- * How an app describes its own settings, so something else can ask about them.
2
+ * How an app describes its own settings — as data, so anything can read it.
3
3
  *
4
- * The point is that the app owns the description. `busybar-flights` knows that
5
- * a route reads `SVO-JFK` and that a departure is a local time without a zone;
6
- * an editor that had to know those things for every app would need editing
7
- * every time a new app appeared. With a spec, installing a package is enough.
4
+ * The app owns the description. `busybar-flights` knows that a route reads
5
+ * `SVO-JFK` and that a departure is a local time with no zone; an editor that
6
+ * had to know that for every app would need editing every time a new app
7
+ * appeared. With a spec, installing a package is enough.
8
8
  *
9
- * Specs are modules rather than JSON because the useful parts — how one entry
10
- * reads in a picker, whether a value is acceptable — are functions. An app
11
- * points at its own with a `busybar.config` field in package.json:
9
+ * Everything here is JSON. Checks are rules rather than functions and a list
10
+ * entry's one-line summary is a template rather than a callback, so a browser
11
+ * or a Dart client can interpret a spec without running the app's JavaScript.
12
+ * An app points at its own from package.json:
12
13
  *
13
- * "busybar": { "config": "./dist/config-spec.js" }
14
+ * "busybar": {
15
+ * "config": "./dist/config-spec.js",
16
+ * "configJson": "./dist/config-spec.json"
17
+ * }
14
18
  */
15
19
  export type FieldType = 'text' | 'secret' | 'number' | 'boolean' | 'select' | 'datetime' | 'path';
16
20
  export type FieldOption = {
@@ -18,6 +22,24 @@ export type FieldOption = {
18
22
  label: string;
19
23
  hint?: string;
20
24
  };
25
+ /** Checks a client interprets. Never code — see `validateValue`. */
26
+ export type ValidationRule = {
27
+ kind: 'required';
28
+ message?: string;
29
+ } | {
30
+ kind: 'pattern';
31
+ pattern: string;
32
+ message: string;
33
+ } | {
34
+ kind: 'integer';
35
+ min: number;
36
+ max: number;
37
+ message?: string;
38
+ } | {
39
+ kind: 'datetime';
40
+ format: 'local';
41
+ message?: string;
42
+ };
21
43
  export type ConfigField = {
22
44
  /** The env variable, or the key inside a record. */
23
45
  key: string;
@@ -34,9 +56,14 @@ export type ConfigField = {
34
56
  options?: readonly FieldOption[];
35
57
  /** What the app itself would do with the setting left out. */
36
58
  fallback?: string;
37
- /** A message when the answer will not do, or nothing when it will. */
38
- validate?: (value: string) => string | undefined;
59
+ rules?: ValidationRule[];
39
60
  };
61
+ /**
62
+ * Whether a change lands without help. Declared by the app because only the
63
+ * app knows: dota re-reads its schedule on every poll, everything else reads
64
+ * its settings once, at startup.
65
+ */
66
+ export type Reloads = 'live' | 'restart';
40
67
  /** A file of records you add to, edit and remove — flights, fixtures. */
41
68
  export type ListSection = {
42
69
  kind: 'list';
@@ -51,11 +78,12 @@ export type ListSection = {
51
78
  at?: string;
52
79
  /** Settings that live beside the array in the same file, not in `.env`. */
53
80
  header?: ConfigField[];
54
- /** What one entry looks like in the picker. */
55
- summary: (entry: Record<string, string>) => string;
81
+ /** How one entry reads in a picker. See `renderSummary` for the syntax. */
82
+ summary: string;
56
83
  fields: ConfigField[];
57
84
  /** Shown instead of the picker when the file has nothing in it yet. */
58
85
  empty?: string;
86
+ reloads?: Reloads;
59
87
  };
60
88
  /** Plain settings, one key to a line. */
61
89
  export type EnvSection = {
@@ -63,19 +91,35 @@ export type EnvSection = {
63
91
  file: string;
64
92
  title: string;
65
93
  fields: ConfigField[];
94
+ reloads?: Reloads;
66
95
  };
67
96
  export type ConfigSection = ListSection | EnvSection;
68
97
  export type AppConfigSpec = {
98
+ /** The contract version. A client refuses one it does not know. */
99
+ specVersion: number;
69
100
  /** The `application_name` the app draws with — the join with everything else. */
70
101
  name: string;
71
102
  /** One line about what the app is, for the app picker. */
72
103
  summary?: string;
73
104
  sections: ConfigSection[];
74
105
  };
75
- /** Identity, for the types. Specs are data; this only saves the annotation. */
76
- export declare function defineConfigSpec(spec: AppConfigSpec): AppConfigSpec;
77
- export declare function required(label: string): (value: string) => string | undefined;
78
- export declare function integerIn(min: number, max: number): (value: string) => string | undefined;
79
- export declare function matching(pattern: RegExp, message: string): (value: string) => string | undefined;
80
- export declare function localDateTime(value: string): "YYYY-MM-DD HH:MM" | "that is not a real date" | undefined;
106
+ export declare const SPEC_VERSION = 1;
107
+ /**
108
+ * Stamps the current version onto a spec, so an author cannot get it wrong and
109
+ * a client always has one to check.
110
+ */
111
+ export declare function defineConfigSpec(spec: Omit<AppConfigSpec, 'specVersion'>): AppConfigSpec;
112
+ /**
113
+ * Reads a spec that came from outside — a file, a package, an HTTP response.
114
+ * The version check is the whole point: once something else reads these, the
115
+ * shape is a contract, and a client that guesses at an unknown one is worse
116
+ * than a client that says which package needs updating.
117
+ */
118
+ export declare function parseSpecJson(text: string, source?: string): AppConfigSpec;
119
+ export declare function parseSpec(raw: unknown, source?: string): AppConfigSpec;
120
+ export declare function required(label?: string): ValidationRule;
121
+ export declare function integerIn(min: number, max: number, message?: string): ValidationRule;
122
+ export declare function matching(pattern: RegExp | string, message: string): ValidationRule;
123
+ /** `2026-09-08 19:30` — a local time at the airport, with no zone on it. */
124
+ export declare const localDateTime: ValidationRule;
81
125
  //# sourceMappingURL=config-spec.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-spec.d.ts","sourceRoot":"","sources":["../src/config-spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,SAAS,GACnB,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC;AAE5E,MAAM,MAAM,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1E,MAAM,MAAM,WAAW,GAAG;IACxB,oDAAoD;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,CAAC;IAChB,mCAAmC;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oBAAoB;IACpB,OAAO,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IACjC,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAClD,CAAC;AAEF,yEAAyE;AACzE,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,2EAA2E;IAC3E,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,+CAA+C;IAC/C,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC;IACnD,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,yCAAyC;AACzC,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG;IAC1B,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B,CAAC;AAEF,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CAEnE;AAID,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,IAC5B,OAAO,MAAM,wBACtB;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,IACxC,OAAO,MAAM,wBAWtB;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,IAC/C,OAAO,MAAM,wBAEtB;AAQD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,8DAW1C"}
1
+ {"version":3,"file":"config-spec.d.ts","sourceRoot":"","sources":["../src/config-spec.ts"],"names":[],"mappings":"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"}
@@ -1,54 +1,77 @@
1
1
  /**
2
- * How an app describes its own settings, so something else can ask about them.
2
+ * How an app describes its own settings — as data, so anything can read it.
3
3
  *
4
- * The point is that the app owns the description. `busybar-flights` knows that
5
- * a route reads `SVO-JFK` and that a departure is a local time without a zone;
6
- * an editor that had to know those things for every app would need editing
7
- * every time a new app appeared. With a spec, installing a package is enough.
4
+ * The app owns the description. `busybar-flights` knows that a route reads
5
+ * `SVO-JFK` and that a departure is a local time with no zone; an editor that
6
+ * had to know that for every app would need editing every time a new app
7
+ * appeared. With a spec, installing a package is enough.
8
8
  *
9
- * Specs are modules rather than JSON because the useful parts — how one entry
10
- * reads in a picker, whether a value is acceptable — are functions. An app
11
- * points at its own with a `busybar.config` field in package.json:
9
+ * Everything here is JSON. Checks are rules rather than functions and a list
10
+ * entry's one-line summary is a template rather than a callback, so a browser
11
+ * or a Dart client can interpret a spec without running the app's JavaScript.
12
+ * An app points at its own from package.json:
12
13
  *
13
- * "busybar": { "config": "./dist/config-spec.js" }
14
+ * "busybar": {
15
+ * "config": "./dist/config-spec.js",
16
+ * "configJson": "./dist/config-spec.json"
17
+ * }
18
+ */
19
+ export const SPEC_VERSION = 1;
20
+ /**
21
+ * Stamps the current version onto a spec, so an author cannot get it wrong and
22
+ * a client always has one to check.
14
23
  */
15
- /** Identity, for the types. Specs are data; this only saves the annotation. */
16
24
  export function defineConfigSpec(spec) {
17
- return spec;
18
- }
19
- // --- Validators the apps kept writing for themselves -------------------------
20
- export function required(label) {
21
- return (value) => (value.trim() ? undefined : `${label} is needed`);
22
- }
23
- export function integerIn(min, max) {
24
- return (value) => {
25
- if (!value.trim()) {
26
- return undefined;
27
- }
28
- const parsed = Number(value);
29
- if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) {
30
- return 'a whole number, please';
31
- }
32
- return parsed < min || parsed > max ? `between ${min} and ${max}` : undefined;
33
- };
34
- }
35
- export function matching(pattern, message) {
36
- return (value) => !value.trim() || pattern.test(value.trim()) ? undefined : message;
25
+ return { specVersion: SPEC_VERSION, ...spec };
37
26
  }
38
27
  /**
39
- * `2026-09-08 19:30` — a local time at the airport, with no zone on it, which
40
- * is how departure boards and boarding passes are written.
28
+ * Reads a spec that came from outside — a file, a package, an HTTP response.
29
+ * The version check is the whole point: once something else reads these, the
30
+ * shape is a contract, and a client that guesses at an unknown one is worse
31
+ * than a client that says which package needs updating.
41
32
  */
42
- const LOCAL_DATETIME = /^\d{4}-\d{2}-\d{2}[T ]\d{1,2}:\d{2}$/;
43
- export function localDateTime(value) {
44
- if (!value.trim()) {
45
- return undefined;
33
+ export function parseSpecJson(text, source = 'spec') {
34
+ let raw;
35
+ try {
36
+ raw = JSON.parse(text);
46
37
  }
47
- if (!LOCAL_DATETIME.test(value.trim())) {
48
- return 'YYYY-MM-DD HH:MM';
38
+ catch (error) {
39
+ throw new Error(`${source}: not valid JSON`, { cause: error });
40
+ }
41
+ return parseSpec(raw, source);
42
+ }
43
+ export function parseSpec(raw, source = 'spec') {
44
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
45
+ throw new Error(`${source}: a spec must be an object`);
49
46
  }
50
- return Number.isNaN(Date.parse(value.trim().replace(' ', 'T')))
51
- ? 'that is not a real date'
52
- : undefined;
47
+ const candidate = raw;
48
+ if (candidate.specVersion !== SPEC_VERSION) {
49
+ throw new Error(`${source}: specVersion ${String(candidate.specVersion)} is not ${SPEC_VERSION} — update whichever side is older`);
50
+ }
51
+ if (typeof candidate.name !== 'string' || !candidate.name.trim()) {
52
+ throw new Error(`${source}: a spec needs the application name it configures`);
53
+ }
54
+ if (!Array.isArray(candidate.sections)) {
55
+ throw new Error(`${source}: a spec needs a sections array`);
56
+ }
57
+ return candidate;
58
+ }
59
+ // --- Rules, written as data ---------------------------------------------------
60
+ export function required(label) {
61
+ return label
62
+ ? { kind: 'required', message: `${label} is needed` }
63
+ : { kind: 'required' };
64
+ }
65
+ export function integerIn(min, max, message) {
66
+ return message ? { kind: 'integer', min, max, message } : { kind: 'integer', min, max };
67
+ }
68
+ export function matching(pattern, message) {
69
+ return {
70
+ kind: 'pattern',
71
+ pattern: typeof pattern === 'string' ? pattern : pattern.source,
72
+ message,
73
+ };
53
74
  }
75
+ /** `2026-09-08 19:30` — a local time at the airport, with no zone on it. */
76
+ export const localDateTime = { kind: 'datetime', format: 'local' };
54
77
  //# sourceMappingURL=config-spec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-spec.js","sourceRoot":"","sources":["../src/config-spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAkEH,+EAA+E;AAC/E,MAAM,UAAU,gBAAgB,CAAC,IAAmB;IAClD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gFAAgF;AAEhF,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,OAAO,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,KAAK,YAAY,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,GAAW;IAChD,OAAO,CAAC,KAAa,EAAE,EAAE;QACvB,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YAClB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1D,OAAO,wBAAwB,CAAC;QAClC,CAAC;QAED,OAAO,MAAM,GAAG,GAAG,IAAI,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,WAAW,GAAG,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAChF,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,OAAe;IACvD,OAAO,CAAC,KAAa,EAAE,EAAE,CACvB,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;AACtE,CAAC;AAED;;;GAGG;AACH,MAAM,cAAc,GAAG,sCAAsC,CAAC;AAE9D,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAClB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;QACvC,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QAC7D,CAAC,CAAC,yBAAyB;QAC3B,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"config-spec.js","sourceRoot":"","sources":["../src/config-spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAmFH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC;AAE9B;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAwC;IAExC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,IAAI,EAAE,CAAC;AAChD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,MAAM,GAAG,MAAM;IACzD,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,kBAAkB,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAY,EAAE,MAAM,GAAG,MAAM;IACrD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,4BAA4B,CAAC,CAAC;IACzD,CAAC;IACD,MAAM,SAAS,GAAG,GAA6B,CAAC;IAEhD,IAAI,SAAS,CAAC,WAAW,KAAK,YAAY,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,GAAG,MAAM,iBAAiB,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,WAAW,YAAY,mCAAmC,CAClH,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,mDAAmD,CAAC,CAAC;IAChF,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,iCAAiC,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO,SAA0B,CAAC;AACpC,CAAC;AAED,iFAAiF;AAEjF,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,KAAK;QACV,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,KAAK,YAAY,EAAE;QACrD,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,GAAW,EAAE,OAAgB;IAClE,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAC1F,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAwB,EAAE,OAAe;IAChE,OAAO;QACL,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM;QAC/D,OAAO;KACR,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,MAAM,aAAa,GAAmB,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC"}
@@ -1,6 +1,8 @@
1
1
  import { Bitmap } from '../preview/png.js';
2
2
  import { type RgbaImage } from './dither.js';
3
3
  export * from './dither.js';
4
+ export * from './png.js';
5
+ export * from './resize.js';
4
6
  export type PanelImageOptions = {
5
7
  /** Square side in pixels; the panel's own height by default. */
6
8
  size?: number;
@@ -12,8 +14,9 @@ export type PanelImageOptions = {
12
14
  * A picture, ready for the back panel: square, grey, dithered to the levels the
13
15
  * device can show. `toPng()` on the result is what `AssetsUpload` wants.
14
16
  *
15
- * Decoding is deliberately not here — JPEG and PNG decoders are a dependency,
16
- * and which one an app pays for is the app's business. Hand this the pixels.
17
+ * PNG arrives decoded by `decodePng` next door, which costs nothing because it
18
+ * is hand-rolled. JPEG stays the app's business: a decoder for it is a real
19
+ * dependency, and which one an app pays for is not this package's call.
17
20
  */
18
21
  export declare function ditherToPanel(image: RgbaImage, options?: PanelImageOptions): Bitmap;
19
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/image/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,UAAU,CAAC;AAElB,cAAc,UAAU,CAAC;AAEzB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAevF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/image/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,UAAU,CAAC;AAElB,cAAc,UAAU,CAAC;AACzB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AAEzB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAevF"}
@@ -2,12 +2,15 @@ import { BACK } from '../device.js';
2
2
  import { Bitmap } from '../preview/png.js';
3
3
  import { autoContrast, ditherToShades, SHADES, toGreyTile, } from './dither.js';
4
4
  export * from './dither.js';
5
+ export * from './png.js';
6
+ export * from './resize.js';
5
7
  /**
6
8
  * A picture, ready for the back panel: square, grey, dithered to the levels the
7
9
  * device can show. `toPng()` on the result is what `AssetsUpload` wants.
8
10
  *
9
- * Decoding is deliberately not here — JPEG and PNG decoders are a dependency,
10
- * and which one an app pays for is the app's business. Hand this the pixels.
11
+ * PNG arrives decoded by `decodePng` next door, which costs nothing because it
12
+ * is hand-rolled. JPEG stays the app's business: a decoder for it is a real
13
+ * dependency, and which one an app pays for is not this package's call.
11
14
  */
12
15
  export function ditherToPanel(image, options = {}) {
13
16
  const size = options.size ?? BACK.height;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/image/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EACL,YAAY,EACZ,cAAc,EACd,MAAM,EACN,UAAU,GAEX,MAAM,UAAU,CAAC;AAElB,cAAc,UAAU,CAAC;AAUzB;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,KAAgB,EAAE,UAA6B,EAAE;IAC7E,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC;IACzC,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACxE,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC;IAClE,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAEtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;YACxC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/image/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EACL,YAAY,EACZ,cAAc,EACd,MAAM,EACN,UAAU,GAEX,MAAM,UAAU,CAAC;AAElB,cAAc,UAAU,CAAC;AACzB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AAUzB;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,KAAgB,EAAE,UAA6B,EAAE;IAC7E,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC;IACzC,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACxE,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC;IAClE,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAEtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;YACxC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { RgbaImage } from './dither.js';
2
+ /**
3
+ * Enough of a PNG decoder for album art: 8- and 16-bit samples, every colour
4
+ * type, no interlacing. busybar-kit already hand-rolls the encoder, so this
5
+ * keeps the pair symmetrical instead of pulling in a decoder dependency for
6
+ * the minority of covers that are not JPEG.
7
+ */
8
+ export declare function decodePng(bytes: Buffer): RgbaImage;
9
+ //# sourceMappingURL=png.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"png.d.ts","sourceRoot":"","sources":["../../src/image/png.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAM1C;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CA6ClD"}
@@ -0,0 +1,165 @@
1
+ import { inflateSync } from 'node:zlib';
2
+ const SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
3
+ const CHANNELS = { 0: 1, 2: 3, 3: 1, 4: 2, 6: 4 };
4
+ /**
5
+ * Enough of a PNG decoder for album art: 8- and 16-bit samples, every colour
6
+ * type, no interlacing. busybar-kit already hand-rolls the encoder, so this
7
+ * keeps the pair symmetrical instead of pulling in a decoder dependency for
8
+ * the minority of covers that are not JPEG.
9
+ */
10
+ export function decodePng(bytes) {
11
+ if (bytes.length < 8 || SIGNATURE.some((byte, index) => bytes[index] !== byte)) {
12
+ throw new Error('not a PNG');
13
+ }
14
+ let header = null;
15
+ let palette = null;
16
+ let transparency = null;
17
+ const parts = [];
18
+ for (let offset = 8; offset + 8 <= bytes.length;) {
19
+ const length = bytes.readUInt32BE(offset);
20
+ const type = bytes.toString('ascii', offset + 4, offset + 8);
21
+ const start = offset + 8;
22
+ const end = start + length;
23
+ if (end > bytes.length) {
24
+ break;
25
+ }
26
+ if (type === 'IHDR') {
27
+ header = readHeader(bytes.subarray(start, end));
28
+ }
29
+ else if (type === 'PLTE') {
30
+ palette = bytes.subarray(start, end);
31
+ }
32
+ else if (type === 'tRNS') {
33
+ transparency = bytes.subarray(start, end);
34
+ }
35
+ else if (type === 'IDAT') {
36
+ parts.push(bytes.subarray(start, end));
37
+ }
38
+ else if (type === 'IEND') {
39
+ break;
40
+ }
41
+ offset = end + 4;
42
+ }
43
+ if (!header) {
44
+ throw new Error('PNG has no header');
45
+ }
46
+ if (parts.length === 0) {
47
+ throw new Error('PNG has no pixel data');
48
+ }
49
+ const raw = inflateSync(Buffer.concat(parts));
50
+ const samples = unfilter(raw, header);
51
+ return expand(samples, header, palette, transparency);
52
+ }
53
+ function readHeader(chunk) {
54
+ const width = chunk.readUInt32BE(0);
55
+ const height = chunk.readUInt32BE(4);
56
+ const depth = chunk[8] ?? 0;
57
+ const colorType = chunk[9] ?? 0;
58
+ const interlace = chunk[12] ?? 0;
59
+ const channels = CHANNELS[colorType];
60
+ if (!channels) {
61
+ throw new Error(`unsupported PNG colour type ${colorType}`);
62
+ }
63
+ if (depth !== 8 && depth !== 16) {
64
+ throw new Error(`unsupported PNG bit depth ${depth}`);
65
+ }
66
+ if (interlace !== 0) {
67
+ throw new Error('interlaced PNG is not supported');
68
+ }
69
+ if (width <= 0 || height <= 0) {
70
+ throw new Error('PNG has no size');
71
+ }
72
+ const bytesPerSample = depth / 8;
73
+ const bpp = channels * bytesPerSample;
74
+ return { width, height, depth, colorType, channels, bpp, stride: width * bpp };
75
+ }
76
+ function unfilter(raw, header) {
77
+ const { stride, bpp, height } = header;
78
+ const out = Buffer.alloc(stride * height);
79
+ for (let y = 0; y < height; y += 1) {
80
+ const filterOffset = y * (stride + 1);
81
+ const filter = raw[filterOffset] ?? 0;
82
+ const line = filterOffset + 1;
83
+ const target = y * stride;
84
+ for (let x = 0; x < stride; x += 1) {
85
+ const value = raw[line + x] ?? 0;
86
+ const left = x >= bpp ? (out[target + x - bpp] ?? 0) : 0;
87
+ const up = y > 0 ? (out[target - stride + x] ?? 0) : 0;
88
+ const upLeft = y > 0 && x >= bpp ? (out[target - stride + x - bpp] ?? 0) : 0;
89
+ out[target + x] = (value + reconstruct(filter, left, up, upLeft)) & 0xff;
90
+ }
91
+ }
92
+ return out;
93
+ }
94
+ function reconstruct(filter, left, up, upLeft) {
95
+ if (filter === 1) {
96
+ return left;
97
+ }
98
+ if (filter === 2) {
99
+ return up;
100
+ }
101
+ if (filter === 3) {
102
+ return (left + up) >> 1;
103
+ }
104
+ if (filter === 4) {
105
+ return paeth(left, up, upLeft);
106
+ }
107
+ return 0;
108
+ }
109
+ function paeth(a, b, c) {
110
+ const p = a + b - c;
111
+ const pa = Math.abs(p - a);
112
+ const pb = Math.abs(p - b);
113
+ const pc = Math.abs(p - c);
114
+ if (pa <= pb && pa <= pc) {
115
+ return a;
116
+ }
117
+ return pb <= pc ? b : c;
118
+ }
119
+ function expand(samples, header, palette, transparency) {
120
+ const { width, height, depth, colorType, channels } = header;
121
+ const step = depth / 8;
122
+ const data = new Uint8Array(width * height * 4);
123
+ for (let pixel = 0; pixel < width * height; pixel += 1) {
124
+ // 16-bit samples are read by their high byte: the panel has 16 grey levels,
125
+ // so the low byte cannot survive the trip anyway.
126
+ const base = pixel * channels * step;
127
+ const at = (index) => samples[base + index * step] ?? 0;
128
+ const out = pixel * 4;
129
+ let r;
130
+ let g;
131
+ let b;
132
+ let a = 255;
133
+ if (colorType === 0) {
134
+ r = g = b = at(0);
135
+ }
136
+ else if (colorType === 2) {
137
+ r = at(0);
138
+ g = at(1);
139
+ b = at(2);
140
+ }
141
+ else if (colorType === 3) {
142
+ const index = at(0);
143
+ r = palette?.[index * 3] ?? 0;
144
+ g = palette?.[index * 3 + 1] ?? 0;
145
+ b = palette?.[index * 3 + 2] ?? 0;
146
+ a = transparency?.[index] ?? 255;
147
+ }
148
+ else if (colorType === 4) {
149
+ r = g = b = at(0);
150
+ a = at(1);
151
+ }
152
+ else {
153
+ r = at(0);
154
+ g = at(1);
155
+ b = at(2);
156
+ a = at(3);
157
+ }
158
+ data[out] = r;
159
+ data[out + 1] = g;
160
+ data[out + 2] = b;
161
+ data[out + 3] = a;
162
+ }
163
+ return { width, height, data };
164
+ }
165
+ //# sourceMappingURL=png.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"png.js","sourceRoot":"","sources":["../../src/image/png.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { RgbaImage } from './dither.js';
2
+ /**
3
+ * Centre-crops to a square and box-filters down to `size`, in colour.
4
+ *
5
+ * The grey path next door does the same walk and throws the channels away at
6
+ * the end; a panel that can show colour wants them kept. Averaging happens in
7
+ * linear light for the same reason it does there — averaging gamma-encoded
8
+ * bytes is why a downscaled picture usually comes out muddier than the
9
+ * original. Alpha is averaged as-is, being linear already.
10
+ */
11
+ export declare function toRgbaTile(image: RgbaImage, size: number): RgbaImage;
12
+ //# sourceMappingURL=resize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resize.d.ts","sourceRoot":"","sources":["../../src/image/resize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAK1C;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAyBpE"}
@@ -0,0 +1,83 @@
1
+ const TO_LINEAR = buildLinearTable();
2
+ const TO_SRGB = buildSrgbTable();
3
+ /**
4
+ * Centre-crops to a square and box-filters down to `size`, in colour.
5
+ *
6
+ * The grey path next door does the same walk and throws the channels away at
7
+ * the end; a panel that can show colour wants them kept. Averaging happens in
8
+ * linear light for the same reason it does there — averaging gamma-encoded
9
+ * bytes is why a downscaled picture usually comes out muddier than the
10
+ * original. Alpha is averaged as-is, being linear already.
11
+ */
12
+ export function toRgbaTile(image, size) {
13
+ const side = Math.min(image.width, image.height);
14
+ const offsetX = Math.floor((image.width - side) / 2);
15
+ const offsetY = Math.floor((image.height - side) / 2);
16
+ const data = new Uint8Array(size * size * 4);
17
+ for (let y = 0; y < size; y += 1) {
18
+ const startY = Math.floor((y * side) / size);
19
+ const endY = Math.max(startY + 1, Math.floor(((y + 1) * side) / size));
20
+ for (let x = 0; x < size; x += 1) {
21
+ const startX = Math.floor((x * side) / size);
22
+ const endX = Math.max(startX + 1, Math.floor(((x + 1) * side) / size));
23
+ const pixel = boxAverage(image, offsetX + startX, offsetY + startY, offsetX + endX, offsetY + endY);
24
+ data.set(pixel, (y * size + x) * 4);
25
+ }
26
+ }
27
+ return { width: size, height: size, data };
28
+ }
29
+ function boxAverage(image, startX, startY, endX, endY) {
30
+ let red = 0;
31
+ let green = 0;
32
+ let blue = 0;
33
+ let alpha = 0;
34
+ let count = 0;
35
+ for (let y = startY; y < endY; y += 1) {
36
+ if (y < 0 || y >= image.height) {
37
+ continue;
38
+ }
39
+ for (let x = startX; x < endX; x += 1) {
40
+ if (x < 0 || x >= image.width) {
41
+ continue;
42
+ }
43
+ const offset = (y * image.width + x) * 4;
44
+ red += TO_LINEAR[image.data[offset] ?? 0] ?? 0;
45
+ green += TO_LINEAR[image.data[offset + 1] ?? 0] ?? 0;
46
+ blue += TO_LINEAR[image.data[offset + 2] ?? 0] ?? 0;
47
+ alpha += image.data[offset + 3] ?? 0;
48
+ count += 1;
49
+ }
50
+ }
51
+ if (count === 0) {
52
+ return [0, 0, 0, 0];
53
+ }
54
+ return [
55
+ encode(red / count),
56
+ encode(green / count),
57
+ encode(blue / count),
58
+ Math.round(alpha / count),
59
+ ];
60
+ }
61
+ function encode(linear) {
62
+ return TO_SRGB[Math.max(0, Math.min(4095, Math.round(linear * 4095)))] ?? 0;
63
+ }
64
+ function buildLinearTable() {
65
+ const table = new Float64Array(256);
66
+ for (let value = 0; value < 256; value += 1) {
67
+ const channel = value / 255;
68
+ table[value] =
69
+ channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
70
+ }
71
+ return table;
72
+ }
73
+ /** The inverse, at 12 bits — enough that rounding never costs a visible level. */
74
+ function buildSrgbTable() {
75
+ const table = new Uint8Array(4096);
76
+ for (let step = 0; step < 4096; step += 1) {
77
+ const linear = step / 4095;
78
+ const encoded = linear <= 0.0031308 ? linear * 12.92 : 1.055 * linear ** (1 / 2.4) - 0.055;
79
+ table[step] = Math.round(Math.max(0, Math.min(1, encoded)) * 255);
80
+ }
81
+ return table;
82
+ }
83
+ //# sourceMappingURL=resize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resize.js","sourceRoot":"","sources":["../../src/image/resize.ts"],"names":[],"mappings":"AAEA,MAAM,SAAS,GAAG,gBAAgB,EAAE,CAAC;AACrC,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;AAEjC;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,KAAgB,EAAE,IAAY;IACvD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IAE7C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QAEvE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;YAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;YACvE,MAAM,KAAK,GAAG,UAAU,CACtB,KAAK,EACL,OAAO,GAAG,MAAM,EAChB,OAAO,GAAG,MAAM,EAChB,OAAO,GAAG,IAAI,EACd,OAAO,GAAG,IAAI,CACf,CAAC;YACF,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC7C,CAAC;AAED,SAAS,UAAU,CACjB,KAAgB,EAChB,MAAc,EACd,MAAc,EACd,IAAY,EACZ,IAAY;IAEZ,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,IAAI,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YAC/B,SAAS;QACX,CAAC;QAED,KAAK,IAAI,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBAC9B,SAAS;YACX,CAAC;YACD,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACzC,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/C,KAAK,IAAI,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;YACrD,IAAI,IAAI,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;YACpD,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;YACrC,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IAED,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACtB,CAAC;IAED,OAAO;QACL,MAAM,CAAC,GAAG,GAAG,KAAK,CAAC;QACnB,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;QACrB,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;KAC1B,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,MAAc;IAC5B,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,gBAAgB;IACvB,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC;IACpC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,GAAG,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC5C,MAAM,OAAO,GAAG,KAAK,GAAG,GAAG,CAAC;QAC5B,KAAK,CAAC,KAAK,CAAC;YACV,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC;IAC9E,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,kFAAkF;AAClF,SAAS,cAAc;IACrB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IACnC,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;QAC3B,MAAM,OAAO,GACX,MAAM,IAAI,SAAS,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,MAAM,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC;QAC7E,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;IACpE,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}