@kispace-io/extension-howto-system 0.8.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.
@@ -0,0 +1,98 @@
1
+ import { Contribution } from '@kispace-io/core';
2
+
3
+ /**
4
+ * HowTo Contribution System
5
+ *
6
+ * Allows extensions to register step-by-step workflows that guide users
7
+ * through specific processes with pre and post condition checks.
8
+ */
9
+
10
+ /**
11
+ * Condition function that checks if a requirement is met or outcome is achieved.
12
+ * Returns a promise that resolves to true if the condition is satisfied.
13
+ */
14
+ export type ConditionFunction = () => Promise<boolean> | boolean;
15
+
16
+ /**
17
+ * A single step in a HowTo workflow
18
+ */
19
+ export interface HowToStep {
20
+ /** Unique identifier for this step */
21
+ id: string;
22
+
23
+ /** Human-readable title of the step */
24
+ title: string;
25
+
26
+ /** Detailed description of what the user should do in this step */
27
+ description: string;
28
+
29
+ /**
30
+ * Pre-condition check: verifies that requirements are met before this step can be executed.
31
+ * If this returns false, the step cannot be started.
32
+ */
33
+ preCondition?: ConditionFunction;
34
+
35
+ /**
36
+ * Post-condition check: verifies that the step was completed successfully.
37
+ * If this returns false, the step is not considered complete.
38
+ */
39
+ postCondition?: ConditionFunction;
40
+
41
+ /** Optional command to execute when the step is activated */
42
+ command?: string;
43
+
44
+ /** Optional command parameters */
45
+ commandParams?: Record<string, any>;
46
+
47
+ /** Whether this step is optional */
48
+ optional?: boolean;
49
+ }
50
+
51
+ /**
52
+ * Context provided to HowTo initialization function
53
+ */
54
+ export interface HowToContext {
55
+ /** Callback to notify the panel that conditions should be re-checked */
56
+ requestUpdate: () => void;
57
+
58
+ /** The contribution ID of this HowTo */
59
+ contributionId: string;
60
+ }
61
+
62
+ /**
63
+ * Initialization function for a HowTo
64
+ * Allows the HowTo to set up its own subscriptions and manage its lifecycle
65
+ */
66
+ export type HowToInitializeFunction = (context: HowToContext) => (() => void) | void;
67
+
68
+ /**
69
+ * A HowTo Contribution defines a complete workflow with multiple steps.
70
+ * Extends Contribution to work with the contribution registry.
71
+ */
72
+ export interface HowToContribution extends Contribution {
73
+ /** Unique identifier for this HowTo */
74
+ id: string;
75
+
76
+ /** Human-readable title of the workflow (can be a function for dynamic values) */
77
+ title: string | (() => string);
78
+
79
+ /** Description of what this workflow helps accomplish (can be a function for dynamic values) */
80
+ description?: string | (() => string);
81
+
82
+ /** Icon identifier (FontAwesome or custom icon) */
83
+ icon?: string;
84
+
85
+ /** List of steps in the workflow */
86
+ steps: HowToStep[];
87
+
88
+ /** Optional category for grouping related workflows */
89
+ category?: string;
90
+
91
+ /**
92
+ * Optional initialization function that runs when the HowTo is started.
93
+ * Can set up subscriptions, listeners, etc.
94
+ * Should return a cleanup function (optional) that will be called when the HowTo is closed.
95
+ */
96
+ initialize?: HowToInitializeFunction;
97
+ }
98
+
@@ -0,0 +1,107 @@
1
+ import { createLogger } from '@kispace-io/core';
2
+ import { rootContext } from '@kispace-io/core';
3
+ import { howToService } from './howto-service';
4
+ import { HOWTO_CONTRIBUTION_TARGET } from './howto-service';
5
+ import './contributions/onboarding-howto-contributions';
6
+ import './contributions/ai-setup-howto-contributions';
7
+ import { TOPIC_SHOW_HOWTO_PANEL, TOPIC_TOGGLE_HOWTO_PANEL } from './k-howto-panel';
8
+ import { registerAll } from '@kispace-io/core';
9
+ import { publish } from '@kispace-io/core';
10
+ import { TOOLBAR_BOTTOM_END } from '@kispace-io/core';
11
+ import './k-howto-panel';
12
+
13
+ const logger = createLogger('HowToExtension');
14
+
15
+ /**
16
+ * HowTo System Extension
17
+ *
18
+ * Provides a system for registering and displaying step-by-step workflows
19
+ * that guide users through specific processes with pre and post condition checks.
20
+ *
21
+ * Features:
22
+ * - Register HowToContributions via contribution registry
23
+ * - Floating, draggable UI panel
24
+ * - Sequential step execution
25
+ * - Pre and post condition validation
26
+ * - Step status tracking
27
+ */
28
+ export default function howToExtension(context: any) {
29
+ logger.info('HowTo system extension loaded');
30
+
31
+ // Register the HowTo service in the dependency injection context
32
+ rootContext.put('howToService', howToService);
33
+
34
+ // Ensure the panel is added to the DOM
35
+ const ensurePanelInDOM = () => {
36
+ // Check if panel already exists
37
+ if (document.querySelector('k-howto-panel')) {
38
+ return;
39
+ }
40
+
41
+ // Create and append the panel
42
+ const panel = document.createElement('k-howto-panel');
43
+ document.body.appendChild(panel);
44
+ logger.info('HowTo panel added to DOM');
45
+ };
46
+
47
+ // Add panel to DOM - use requestAnimationFrame to ensure DOM is ready
48
+ if (document.body) {
49
+ requestAnimationFrame(() => {
50
+ ensurePanelInDOM();
51
+ });
52
+ } else {
53
+ // Wait for DOM to be ready
54
+ const checkDOM = () => {
55
+ if (document.body) {
56
+ ensurePanelInDOM();
57
+ } else {
58
+ requestAnimationFrame(checkDOM);
59
+ }
60
+ };
61
+ requestAnimationFrame(checkDOM);
62
+ }
63
+
64
+ // Register command to show/toggle the HowTo panel
65
+ registerAll({
66
+ command: {
67
+ id: 'howto.show-panel',
68
+ name: 'Show HowTo Panel',
69
+ description: 'Shows the HowTo workflows panel',
70
+ icon: 'list-check',
71
+ parameters: []
72
+ },
73
+ handler: {
74
+ execute: () => {
75
+ publish(TOPIC_SHOW_HOWTO_PANEL, null);
76
+ }
77
+ }
78
+ });
79
+
80
+ registerAll({
81
+ command: {
82
+ id: 'howto.toggle-panel',
83
+ name: 'Toggle HowTo Panel',
84
+ description: 'Toggles the visibility of the HowTo workflows panel',
85
+ icon: 'list-check',
86
+ keyBinding: 'CTRL+SHIFT+H',
87
+ parameters: []
88
+ },
89
+ handler: {
90
+ execute: () => {
91
+ publish(TOPIC_TOGGLE_HOWTO_PANEL, null);
92
+ }
93
+ },
94
+ contribution: {
95
+ target: TOOLBAR_BOTTOM_END,
96
+ icon: 'list-check',
97
+ label: 'HowTo',
98
+ }
99
+ });
100
+
101
+ logger.info('HowTo system extension initialized');
102
+ }
103
+
104
+ // Export the service and types for direct imports
105
+ export { howToService, HOWTO_CONTRIBUTION_TARGET } from './howto-service';
106
+ export type { HowToContribution, HowToStep, ConditionFunction } from './howto-contribution';
107
+
@@ -0,0 +1,123 @@
1
+ import { contributionRegistry, TOPIC_CONTRIBUTEIONS_CHANGED } from '@kispace-io/core';
2
+ import { HowToContribution, HowToStep } from './howto-contribution';
3
+ import { subscribe } from '@kispace-io/core';
4
+ import { createLogger } from '@kispace-io/core';
5
+ import { Signal } from '@lit-labs/signals';
6
+
7
+ const logger = createLogger('HowToService');
8
+
9
+ export const HOWTO_CONTRIBUTION_TARGET = 'system.howtos';
10
+
11
+ /**
12
+ * HowTo Service
13
+ *
14
+ * Manages HowTo contributions by reading from the contribution registry.
15
+ * Provides methods to retrieve and manage workflows.
16
+ */
17
+ export class HowToService {
18
+ private contributions: Map<string, HowToContribution> = new Map();
19
+
20
+ constructor() {
21
+ // Load initial contributions
22
+ this.loadContributions();
23
+
24
+ // Subscribe to contribution changes
25
+ subscribe(TOPIC_CONTRIBUTEIONS_CHANGED, (event: any) => {
26
+ if (event.target === HOWTO_CONTRIBUTION_TARGET) {
27
+ this.loadContributions();
28
+ }
29
+ });
30
+ }
31
+
32
+ /**
33
+ * Load contributions from the contribution registry
34
+ */
35
+ private loadContributions(): void {
36
+ const contributions = contributionRegistry.getContributions(HOWTO_CONTRIBUTION_TARGET) as HowToContribution[];
37
+
38
+ this.contributions.clear();
39
+
40
+ for (const contribution of contributions) {
41
+ // Validate contribution
42
+ if (!contribution.id) {
43
+ logger.warn('HowTo contribution missing id, skipping');
44
+ continue;
45
+ }
46
+
47
+ if (!contribution.label) {
48
+ // Use title as label if label is not provided
49
+ // Handle both string and function types
50
+ const title = typeof contribution.title === 'function'
51
+ ? contribution.title()
52
+ : contribution.title;
53
+ contribution.label = title;
54
+ }
55
+
56
+ if (!contribution.steps || contribution.steps.length === 0) {
57
+ logger.warn(`HowTo contribution "${contribution.id}" has no steps, skipping`);
58
+ continue;
59
+ }
60
+
61
+ // Validate step IDs are unique
62
+ const stepIds = new Set<string>();
63
+ for (const step of contribution.steps) {
64
+ if (stepIds.has(step.id)) {
65
+ logger.warn(`HowTo contribution "${contribution.id}" has duplicate step ID: "${step.id}"`);
66
+ continue;
67
+ }
68
+ stepIds.add(step.id);
69
+ }
70
+
71
+ this.contributions.set(contribution.id, contribution);
72
+ const title = typeof contribution.title === 'function'
73
+ ? contribution.title()
74
+ : contribution.title;
75
+ logger.debug(`Loaded HowTo contribution: ${title} (${contribution.id})`);
76
+ }
77
+
78
+ logger.info(`Loaded ${this.contributions.size} HowTo contributions`);
79
+ }
80
+
81
+ /**
82
+ * Get a HowTo Contribution by ID
83
+ *
84
+ * @param contributionId The ID of the contribution
85
+ * @returns The contribution or undefined if not found
86
+ */
87
+ getContribution(contributionId: string): HowToContribution | undefined {
88
+ return this.contributions.get(contributionId);
89
+ }
90
+
91
+ /**
92
+ * Get all registered HowTo Contributions
93
+ *
94
+ * @returns Array of all contributions
95
+ */
96
+ getAllContributions(): HowToContribution[] {
97
+ return Array.from(this.contributions.values());
98
+ }
99
+
100
+ /**
101
+ * Get HowTo Contributions by category
102
+ *
103
+ * @param category The category to filter by
104
+ * @returns Array of contributions in the category
105
+ */
106
+ getContributionsByCategory(category: string): HowToContribution[] {
107
+ return Array.from(this.contributions.values())
108
+ .filter(contrib => contrib.category === category);
109
+ }
110
+
111
+ /**
112
+ * Check if a contribution exists
113
+ *
114
+ * @param contributionId The ID of the contribution
115
+ * @returns True if the contribution exists
116
+ */
117
+ hasContribution(contributionId: string): boolean {
118
+ return this.contributions.has(contributionId);
119
+ }
120
+ }
121
+
122
+ export const howToService = new HowToService();
123
+
package/src/i18n.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "namespace": "extensions",
3
+ "en": {
4
+ "EXT_HOWTO_NAME": "HowTo System",
5
+ "EXT_HOWTO_DESC": "Step-by-step workflow system with pre and post condition checks"
6
+ },
7
+ "de": {
8
+ "EXT_HOWTO_NAME": "HowTo-System",
9
+ "EXT_HOWTO_DESC": "Schritt-für-Schritt-Workflow-System mit Vor- und Nachbedingungsprüfungen"
10
+ }
11
+ }
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { extensionRegistry, i18nLazy, contributionRegistry, SYSTEM_LANGUAGE_BUNDLES } from '@kispace-io/core';
2
+ import bundle from './i18n.json';
3
+
4
+ contributionRegistry.registerContribution(SYSTEM_LANGUAGE_BUNDLES, bundle as any);
5
+
6
+ const t = i18nLazy('extensions');
7
+
8
+ extensionRegistry.registerExtension({
9
+ id: "system.howto",
10
+ name: t('EXT_HOWTO_NAME'),
11
+ description: t('EXT_HOWTO_DESC'),
12
+ loader: () => import("./howto-extension"),
13
+ icon: "list-check",
14
+
15
+
16
+ });