@mxtommy/kip 4.5.0-beta.1 → 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.
- package/CHANGELOG.md +18 -0
- package/README.md +11 -7
- package/package.json +21 -8
- package/plugin/duckdb-parquet-storage.service.js +1182 -0
- package/plugin/history-series.service.js +439 -0
- package/plugin/index.js +705 -30
- package/plugin/openApi.json +253 -3
- package/plugin/plugin-auth.service.js +75 -0
- package/public/assets/help-docs/chartplotter.md +5 -18
- package/public/assets/help-docs/community.md +0 -3
- package/public/assets/help-docs/configuration.md +1 -1
- package/public/assets/help-docs/contact-us.md +0 -4
- package/public/assets/help-docs/dashboards.md +20 -18
- package/public/assets/help-docs/datainspector.md +7 -5
- package/public/assets/help-docs/history-api.md +116 -0
- package/public/assets/help-docs/menu.json +18 -6
- package/public/assets/help-docs/nodered-control-flows.md +125 -0
- package/public/assets/help-docs/putcontrols.md +101 -60
- package/public/assets/help-docs/welcome.md +6 -7
- package/public/assets/help-docs/widget-historical-series.md +66 -0
- package/public/assets/help-docs/zones.md +5 -10
- package/public/chunk-A6DQJFP4.js +16 -0
- package/public/chunk-B75MT7ND.js +1 -0
- package/public/{chunk-T6TFVZVM.js → chunk-CEB42O2C.js} +1 -1
- package/public/chunk-CHGXAEKT.js +2 -0
- package/public/chunk-D7VDX7ZF.js +5 -0
- package/public/{chunk-ZQER6AIQ.js → chunk-DEGYRCMI.js} +1 -1
- package/public/{chunk-M2B5OYGO.js → chunk-DEM56G4S.js} +1 -1
- package/public/chunk-DYTBBUMI.js +4 -0
- package/public/chunk-EQ2N7KDA.js +3 -0
- package/public/chunk-FNF7M3AE.js +1 -0
- package/public/chunk-IHURI4IH.js +5 -0
- package/public/{chunk-YIYYVDFO.js → chunk-IYRLINL7.js} +2 -2
- package/public/{chunk-5FEX27I4.js → chunk-JB4YVVNW.js} +1 -1
- package/public/chunk-JGGMFMY5.js +1 -0
- package/public/chunk-KPHICV76.js +5 -0
- package/public/{chunk-QZKCRH3H.js → chunk-KZ5DUKAX.js} +1 -1
- package/public/{chunk-HMOOTAEA.js → chunk-LQDSU4WS.js} +3 -3
- package/public/{chunk-IXQ7KIFY.js → chunk-MGPPVLZ7.js} +1 -1
- package/public/{chunk-QVCLOCEC.js → chunk-R7RQHWKJ.js} +1 -1
- package/public/chunk-RONXIZ2U.js +9 -0
- package/public/chunk-S72JTJPN.js +6 -0
- package/public/{chunk-KFFAA7DL.js → chunk-VCY32MWT.js} +8 -8
- package/public/chunk-YCEXTKGG.js +1 -0
- package/public/chunk-YKJKIWXO.js +6 -0
- package/public/chunk-ZV7IYYEQ.js +50 -0
- package/public/index.html +1 -1
- package/public/main-FQESQQV6.js +1 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -84
- package/.github/ISSUE_TEMPLATE/config.yml +0 -5
- package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -35
- package/.github/copilot-instructions.md +0 -205
- package/.github/instructions/angular.instructions.md +0 -123
- package/.github/instructions/best-practices.instructions.md +0 -59
- package/.github/instructions/project.instructions.md +0 -432
- package/.github/workflows/ci.yml +0 -37
- package/docs/widget-schematic.md +0 -102
- package/images/ActionSidenav.png +0 -0
- package/images/ChartplotterMode.png +0 -0
- package/images/KIPDemo.png +0 -0
- package/images/KipBrightness-1024.png +0 -0
- package/images/KipConfig-Units-1024.png +0 -0
- package/images/KipConfig-display-1024x488.png +0 -0
- package/images/KipFreeboard-SK-1024.png +0 -0
- package/images/KipGaugeSample1-1024x545.png +0 -0
- package/images/KipGaugeSample2-1024x488.png +0 -0
- package/images/KipGaugeSample3-1024x508.png +0 -0
- package/images/KipNightMode-1024.png +0 -0
- package/images/KipWidgetConfig-layout-1024.png +0 -0
- package/images/KipWidgetConfig-paths-1024x488.png +0 -0
- package/images/Options.png +0 -0
- package/images/exterior_user_installs.png +0 -0
- package/images/formfactor.png +0 -0
- package/public/assets/help-docs/datasets.md +0 -95
- package/public/chunk-2OB7ZJBR.js +0 -3
- package/public/chunk-6GGJZDRE.js +0 -1
- package/public/chunk-6V4GGGXE.js +0 -2
- package/public/chunk-A5BW6BUM.js +0 -1
- package/public/chunk-DGE5YFPU.js +0 -5
- package/public/chunk-G6M3Z3BY.js +0 -53
- package/public/chunk-GMGZLXY7.js +0 -4
- package/public/chunk-GUZ3BDVZ.js +0 -2
- package/public/chunk-ICDGHQFP.js +0 -6
- package/public/chunk-JCNE4QHQ.js +0 -15
- package/public/chunk-K6XYUNG4.js +0 -8
- package/public/chunk-LGCQEN7V.js +0 -4
- package/public/chunk-O3JH7UTR.js +0 -1
- package/public/chunk-Q3USFT4F.js +0 -2
- package/public/chunk-VIKU7BH7.js +0 -1
- package/public/chunk-XMQPXXLW.js +0 -8
- package/public/main-4URMGBQS.js +0 -1
- package/rm-npmjs-beta.sh +0 -50
- package/tools/schematics/collection.json +0 -9
- package/tools/schematics/create-host2-widget/files/readme/README.md.template +0 -109
- package/tools/schematics/create-host2-widget/files/spec/widget-__name@dasherize__.component.spec.ts +0 -38
- package/tools/schematics/create-host2-widget/files/widget/widget-__name@dasherize__.component.html +0 -6
- package/tools/schematics/create-host2-widget/files/widget/widget-__name@dasherize__.component.scss +0 -5
- package/tools/schematics/create-host2-widget/files/widget/widget-__name@dasherize__.component.ts.template +0 -94
- package/tools/schematics/create-host2-widget/index.js +0 -138
- package/tools/schematics/create-host2-widget/schema.json +0 -89
- package/tools/schematics/create-host2-widget/test/create-host2-widget.spec.ts +0 -70
- package/tools/schematics/create-host2-widget/utils/formatting.js +0 -119
|
@@ -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
|
-
};
|