@mxtommy/kip 4.5.0 → 4.5.1

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 (71) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/package.json +16 -6
  3. package/plugin/duckdb-parquet-storage.service.js +78 -102
  4. package/plugin/index.js +60 -40
  5. package/public/{chunk-6XFWUUDD.js → chunk-EQ2N7KDA.js} +1 -1
  6. package/public/{chunk-EDNYYQIZ.js → chunk-IYRLINL7.js} +1 -1
  7. package/public/{chunk-UYIJND2R.js → chunk-JGGMFMY5.js} +1 -1
  8. package/public/{chunk-DD4F6F4S.js → chunk-RONXIZ2U.js} +8 -8
  9. package/public/{chunk-2ICAVOT2.js → chunk-VCY32MWT.js} +1 -1
  10. package/public/{chunk-J3LDKVIS.js → chunk-ZV7IYYEQ.js} +1 -1
  11. package/public/index.html +1 -1
  12. package/public/{main-EG2WF4EO.js → main-FQESQQV6.js} +1 -1
  13. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -84
  14. package/.github/ISSUE_TEMPLATE/config.yml +0 -5
  15. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -35
  16. package/.github/copilot-instructions.md +0 -218
  17. package/.github/instructions/angular.instructions.md +0 -123
  18. package/.github/instructions/best-practices.instructions.md +0 -59
  19. package/.github/instructions/project.instructions.md +0 -468
  20. package/.github/workflows/ci.yml +0 -37
  21. package/docs/widget-schematic.md +0 -102
  22. package/images/ActionSidenav.png +0 -0
  23. package/images/ChartplotterMode.png +0 -0
  24. package/images/KIPDemo.png +0 -0
  25. package/images/KipBrightness-1024.png +0 -0
  26. package/images/KipConfig-Units-1024.png +0 -0
  27. package/images/KipConfig-display-1024x488.png +0 -0
  28. package/images/KipFreeboard-SK-1024.png +0 -0
  29. package/images/KipGaugeSample1-1024x545.png +0 -0
  30. package/images/KipGaugeSample2-1024x488.png +0 -0
  31. package/images/KipGaugeSample3-1024x508.png +0 -0
  32. package/images/KipNightMode-1024.png +0 -0
  33. package/images/KipWidgetConfig-layout-1024.png +0 -0
  34. package/images/KipWidgetConfig-paths-1024x488.png +0 -0
  35. package/images/Options.png +0 -0
  36. package/images/exterior_user_installs.png +0 -0
  37. package/images/formfactor.png +0 -0
  38. package/plugin-config-data/kip/historicalData/kip-history.duckdb +0 -0
  39. package/plugin-config-data/kip/historicalData/parquet/chart-1/1772344583976-1772344583976.parquet +0 -0
  40. package/plugin-config-data/kip/historicalData/parquet/live-1/1771408800000-1771408890000.parquet +0 -0
  41. package/plugin-config-data/kip/historicalData/parquet/live-1/1771412400000-1771412490000.parquet +0 -0
  42. package/plugin-config-data/kip/historicalData/parquet/live-1/1771419600000-1771419650000.parquet +0 -0
  43. package/plugin-config-data/kip/historicalData/parquet/live-1/1772344584154-1772344584154.parquet +0 -0
  44. package/plugin-config-data/kip/historicalData/parquet/live-1/1772344584191-1772344584191.parquet +0 -0
  45. package/plugin-config-data/kip/historicalData/parquet/live-1/1772344584268-1772344584268.parquet +0 -0
  46. package/plugin-config-data/kip/historicalData/parquet/live-2/1771502400000-1771502400000.parquet +0 -0
  47. package/plugin-config-data/kip/historicalData/parquet/live-3/1771408800000-1771408890000.parquet +0 -0
  48. package/plugin-config-data/kip/historicalData/parquet/live-3/1771412400000-1771412490000.parquet +0 -0
  49. package/plugin-config-data/kip/historicalData/parquet/live-3/1771419600000-1771419650000.parquet +0 -0
  50. package/plugin-config-data/kip/historicalData/parquet/live-3/1772344584268-1772344584268.parquet +0 -0
  51. package/plugin-config-data/kip/historicalData/parquet/live-4/1771408800000-1771408890000.parquet +0 -0
  52. package/plugin-config-data/kip/historicalData/parquet/live-4/1771412400000-1771412490000.parquet +0 -0
  53. package/plugin-config-data/kip/historicalData/parquet/live-4/1771419600000-1771419650000.parquet +0 -0
  54. package/plugin-config-data/kip/historicalData/parquet/live-5/1771412400000-1771412490000.parquet +0 -0
  55. package/plugin-config-data/kip/historicalData/parquet/live-5/1771419600000-1771419650000.parquet +0 -0
  56. package/plugin-config-data/kip/historicalData/parquet/live-6/1771419600000-1771419650000.parquet +0 -0
  57. package/plugin-config-data/kip/historicalData/parquet/live-prefixed-1/1771408800000-1771408890000.parquet +0 -0
  58. package/plugin-config-data/kip/historicalData/parquet/live-prefixed-1/1771412400000-1771412490000.parquet +0 -0
  59. package/plugin-config-data/kip/historicalData/parquet/live-prefixed-1/1771419600000-1771419650000.parquet +0 -0
  60. package/plugin-config-data/kip/historicalData/parquet/live-prefixed-1/1772344584191-1772344584191.parquet +0 -0
  61. package/plugin-config-data/kip/historicalData/parquet/live-prefixed-1/1772344584268-1772344584268.parquet +0 -0
  62. package/tools/schematics/collection.json +0 -9
  63. package/tools/schematics/create-host2-widget/files/readme/README.md.template +0 -109
  64. package/tools/schematics/create-host2-widget/files/spec/widget-__name@dasherize__.component.spec.ts +0 -38
  65. package/tools/schematics/create-host2-widget/files/widget/widget-__name@dasherize__.component.html +0 -6
  66. package/tools/schematics/create-host2-widget/files/widget/widget-__name@dasherize__.component.scss +0 -5
  67. package/tools/schematics/create-host2-widget/files/widget/widget-__name@dasherize__.component.ts.template +0 -94
  68. package/tools/schematics/create-host2-widget/index.js +0 -138
  69. package/tools/schematics/create-host2-widget/schema.json +0 -89
  70. package/tools/schematics/create-host2-widget/test/create-host2-widget.spec.ts +0 -70
  71. package/tools/schematics/create-host2-widget/utils/formatting.js +0 -119
@@ -1,89 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema",
3
- "$id": "CreateHost2WidgetSchema",
4
- "title": "Create Host2 Widget",
5
- "type": "object",
6
- "properties": {
7
- "name": {
8
- "type": "string",
9
- "description": "Widget machine name without widget- prefix (kebab-case). e.g. tides-chart",
10
- "minLength": 3,
11
- "x-prompt": "The widget base name is the Angular component machine name (kebab-case, without widget- prefix which will be added automatically). Using base name tides-chart will create a widget-tides-chart component.\nWidget base name:"
12
- },
13
- "title": {
14
- "type": "string",
15
- "description": "The widget title is the descriptive title used in the Add Widget dialog",
16
- "minLength": 3,
17
- "default": "Widget Label",
18
- "x-prompt": "The widget title is the descriptive title used in the Add Widget dialog.\nWidget title:"
19
- },
20
- "registerWidget": {
21
- "type": "string",
22
- "description": "Register widget and make it available in the widget Add Options dialog",
23
- "enum": [
24
- "No",
25
- "Core",
26
- "Gauge",
27
- "Component",
28
- "Racing"
29
- ],
30
- "x-prompt": "Register widget and make it available in the widget Add Options dialog (choose category or 'no' to skip):"
31
- },
32
- "pathType": {
33
- "type": "string",
34
- "description": "The type of value the Signal K path provides (number|string|boolean|Date)",
35
- "enum": [
36
- "number",
37
- "string",
38
- "boolean",
39
- "Date"
40
- ],
41
- "default": "number",
42
- "x-prompt": "Path value type (number|string|boolean|Date):"
43
- },
44
- "pathDefault": {
45
- "type": [
46
- "string",
47
- "null"
48
- ],
49
- "default": null,
50
- "description": "The default Signal K path the widget should use (if any)",
51
- "x-prompt": "Type the default Signal K path value for the widget (press enter for no default):"
52
- },
53
- "zonesSupport": {
54
- "type": "boolean",
55
- "default": false,
56
- "description": "Enable Signal K Zones metadata support.",
57
- "x-prompt": "Enable Signal K Zones metadata support?"
58
- },
59
- "addSpec": {
60
- "type": "boolean",
61
- "default": true,
62
- "description": "Generate widget spec (test) file.",
63
- "x-prompt": "Generate widget tests file?"
64
- },
65
- "todoBlock": {
66
- "type": "boolean",
67
- "default": true,
68
- "description": "Include TODO guidance block in component.",
69
- "x-prompt": "Include TODO guidance block in component?"
70
- },
71
- "readme": {
72
- "type": "boolean",
73
- "default": true,
74
- "description": "Generate developer instruction README file in the widget's folder.",
75
- "x-prompt": "Generate developer instruction README file in the widget's folder?"
76
- },
77
- "debugLogging": {
78
- "type": "boolean",
79
- "default": false,
80
- "description": "Enable verbose debug logging for schematic execution (normalization, prompts, service update)."
81
- }
82
- },
83
- "required": [
84
- "name",
85
- "title",
86
- "registerWidget"
87
- ],
88
- "additionalProperties": false
89
- }
@@ -1,70 +0,0 @@
1
- import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
2
- import { Tree } from '@angular-devkit/schematics';
3
- import * as path from 'path';
4
-
5
- // Path to collection.json
6
- const collectionPath = path.join(__dirname, '..', 'collection.json').replace(/test\/[.][.]/, '..');
7
-
8
- describe('create-host2-widget schematic', () => {
9
- const runner = new SchematicTestRunner('kip-schematics', collectionPath);
10
- let appTree: UnitTestTree;
11
-
12
- beforeEach(() => {
13
- // Minimal host tree with widget.service.ts skeleton to exercise mutations
14
- appTree = new UnitTestTree(Tree.empty());
15
- appTree.create('src/app/core/services/widget.service.ts', `import { Type } from '@angular/core';\nexport interface WidgetDescription { selector: string; componentClassName: string; category: string; name: string; description: string; icon: string; minWidth: number; minHeight: number; defaultWidth: number; defaultHeight: number; requiredPlugins: string[]; }\nexport class WidgetService {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private readonly _componentTypeMap: Record<string, Type<any>> = {\n };\n private readonly _widgetDefinition: readonly WidgetDescription[] = [\n ];\n}`);
16
- });
17
-
18
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
- function run(options: Record<string, any>) {
20
- return runner.runSchematic('create-host2-widget', {
21
- name: 'alpha', title: 'Alpha', registerWidget: 'Core',
22
- pathType: 'number', pathDefault: null, ignoreZones: false,
23
- addSpec: false, todoBlock: false, readme: false,
24
- ...options
25
- }, appTree);
26
- }
27
-
28
- it('generates widget files', async () => {
29
- const tree = await run({});
30
- expect(tree.exists('src/app/widgets/widget-alpha/widget-alpha.component.ts')).toBeTrue();
31
- });
32
-
33
- it('updates service: import, map, definition', async () => {
34
- const tree = await run({});
35
- const service = tree.read('src/app/core/services/widget.service.ts')!.toString('utf-8');
36
- expect(service).toContain("import { WidgetAlphaComponent } from '../../widgets/widget-alpha/widget-alpha.component'");
37
- expect(service).toMatch(/_componentTypeMap[\s\S]*WidgetAlphaComponent: WidgetAlphaComponent/);
38
- expect(service).toMatch(/selector: 'widget-alpha'/);
39
- });
40
-
41
- it('inserts second widget in same category after first', async () => {
42
- let tree = await run({});
43
- appTree = tree; // carry over
44
- tree = await run({ name: 'beta', title: 'Beta' });
45
- const service = tree.read('src/app/core/services/widget.service.ts')!.toString('utf-8');
46
- const alphaIdx = service.indexOf("selector: 'widget-alpha'");
47
- const betaIdx = service.indexOf("selector: 'widget-beta'");
48
- expect(alphaIdx).toBeGreaterThan(-1);
49
- expect(betaIdx).toBeGreaterThan(-1);
50
- expect(betaIdx).toBeGreaterThan(alphaIdx); // inserted after
51
- });
52
-
53
- it('is idempotent (running with same name does not duplicate)', async () => {
54
- let tree = await run({});
55
- appTree = tree;
56
- tree = await run({});
57
- const service = tree.read('src/app/core/services/widget.service.ts')!.toString('utf-8');
58
- const occurrences = (service.match(/selector: 'widget-alpha'/g) || []).length;
59
- expect(occurrences).toBe(1);
60
- });
61
-
62
- it('skips registration when registerWidget = no', async () => {
63
- const tree = await run({ name: 'gamma', title: 'Gamma', registerWidget: 'no' });
64
- const service = tree.read('src/app/core/services/widget.service.ts')!.toString('utf-8');
65
- expect(service).not.toContain('WidgetGammaComponent');
66
- expect(service).not.toContain("selector: 'widget-gamma'");
67
- // component file still generated
68
- expect(tree.exists('src/app/widgets/widget-gamma/widget-gamma.component.ts')).toBeTrue();
69
- });
70
- });
@@ -1,119 +0,0 @@
1
- // JavaScript utility helpers for widget.service.ts mutation formatting & insertion logic
2
- // Converted from TypeScript version.
3
-
4
- function escapeSingle(str) {
5
- return str.replace(/'/g, "\\'");
6
- }
7
-
8
- function buildWidgetDefinitionObject(selector, className, opts) {
9
- return `{
10
- name: '${opts.title}',
11
- description: '${escapeSingle(opts.description)}',
12
- icon: '${opts.icon}', // TODO replace placeholder icon
13
- minWidth: ${opts.minWidth},
14
- minHeight: ${opts.minHeight},
15
- defaultWidth: ${opts.defaultWidth},
16
- defaultHeight: ${opts.defaultHeight},
17
- category: '${opts.category}',
18
- requiredPlugins: [],
19
- selector: '${selector}',
20
- componentClassName: '${className}'
21
- },`;
22
- }
23
-
24
- function appendImport(src, importStatement) {
25
- if (src.includes(importStatement)) return src;
26
- const importBlockRegex = /^(?:import[^;]+;\s*)+/m;
27
- if (importBlockRegex.test(src)) {
28
- return src.replace(importBlockRegex, (block) => {
29
- const normalized = block.replace(/\n*$/, '');
30
- return normalized + '\n' + importStatement + '\n\n';
31
- });
32
- }
33
- return importStatement + '\n\n' + src;
34
- }
35
-
36
- function updateComponentMap(src, className) {
37
- const mapPattern = /_componentTypeMap: Record<string, Type<any>> = {([\s\S]*?)};/m;
38
- return src.replace(mapPattern, (m, inner) => {
39
- if (inner.includes(className + ':')) return m;
40
- const lines = inner.split(/\n/);
41
- while (lines.length && lines[lines.length - 1].trim() === '') lines.pop();
42
- for (let i = lines.length - 1; i >= 0; i--) {
43
- const trimmed = lines[i].trim();
44
- if (!trimmed) continue;
45
- if (!trimmed.includes(':')) break;
46
- if (!trimmed.endsWith(',')) lines[i] = lines[i] + ',';
47
- break;
48
- }
49
- lines.push(` ${className}: ${className}`);
50
- // Replace inner content, then normalize closing brace indentation to two spaces
51
- let replaced = m.replace(inner, lines.join('\n') + '\n');
52
- replaced = replaced.replace(/\n *};/, '\n };');
53
- return replaced;
54
- });
55
- }
56
-
57
- function normalizeDefinitionFormatting(content, baseIndent) {
58
- let rebuilt = content;
59
- // Remove any blank line(s) between objects: ensure pattern `},\n{` with exactly one newline
60
- rebuilt = rebuilt.replace(/},\n\n+(\s*{)/g, '},\n$1');
61
- // Remove blank line before closing array
62
- rebuilt = rebuilt.replace(/,\n\n(\s*])/g, ',\n$1');
63
- // Remove stray duplicate blank lines inside objects
64
- rebuilt = rebuilt.replace(/({\n)([ \t]*\n)+/g, '$1');
65
- rebuilt = rebuilt.replace(/\n\s*\n(\s+\w+:)/g, '\n$1');
66
- // Normalize opening brace indentation
67
- rebuilt = rebuilt.replace(/^(\s*){(?=\n)/gm, baseIndent + '{');
68
- // Collapse any 2+ blank lines to single newline
69
- rebuilt = rebuilt.replace(/\n{2,}/g, '\n');
70
- // Remove any trailing blank lines; do NOT force an extra newline because the array pattern supplies one before the closing ];
71
- return rebuilt.replace(/\n\s*$/,'');
72
- }
73
-
74
- function insertDefinitionObject(src, selector, className, opts) {
75
- const defArrayPattern = /private readonly _widgetDefinition: readonly WidgetDescription\[] = \[(\s*[\s\S]*?)\n\s*];/m;
76
- return src.replace(defArrayPattern, (m, inner) => {
77
- if (inner.includes(`selector: '${selector}'`)) return m;
78
- let defObj = buildWidgetDefinitionObject(selector, className, opts).trim();
79
- defObj = defObj.startsWith('{') ? defObj : '{' + defObj;
80
- defObj = defObj.replace(/\n{2,}/g, '\n');
81
- const linesDef = defObj.split(/\n/).map(l => l.trimEnd());
82
- const openIdx = 0; const closeIdx = linesDef.length - 1;
83
- const indentMatch = inner.match(/^(\s*){/m);
84
- const baseIndent = indentMatch ? indentMatch[1] : ' ';
85
- const firstLine = baseIndent + '{';
86
- const lastLine = baseIndent + linesDef[closeIdx].trim();
87
- const middle = linesDef.slice(openIdx + 1, closeIdx).filter(l => l.trim().length > 0).map(l => baseIndent + ' ' + l.trim());
88
- defObj = [firstLine, ...middle, lastLine].join('\n');
89
- const objectRegex = /{[\s\S]*?},\n?/g;
90
- const objects = inner.match(objectRegex) || [];
91
- if (objects.length === 0) {
92
- return m.replace(inner, defObj + '\n');
93
- }
94
- const metas = objects.map(o => ({ text: o, category: (o.match(/category: '([^']+)'/) || [])[1] }));
95
- let insertAfter = -1;
96
- for (let i = 0; i < metas.length; i++) {
97
- if (metas[i].category === opts.category) insertAfter = i;
98
- }
99
- const formattedNew = defObj + '\n';
100
- let rebuilt = '';
101
- metas.forEach((meta, idx) => {
102
- const normalized = meta.text.endsWith('\n') ? meta.text : meta.text + '\n';
103
- rebuilt += normalized;
104
- if (idx === insertAfter) rebuilt += formattedNew; // inserted immediately, no extra blank line expected
105
- });
106
- if (insertAfter === -1) rebuilt += formattedNew; // appended at end
107
- rebuilt = normalizeDefinitionFormatting(rebuilt, baseIndent);
108
- return m.replace(inner, rebuilt);
109
- });
110
- }
111
-
112
- module.exports = {
113
- escapeSingle,
114
- buildWidgetDefinitionObject,
115
- appendImport,
116
- updateComponentMap,
117
- insertDefinitionObject,
118
- normalizeDefinitionFormatting
119
- };