@fishawack/lab-env 4.45.0-beta.2 → 4.45.0-beta.3

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,422 @@
1
+ ---
2
+ applyTo: "**/*.vue"
3
+ ---
4
+
5
+ # Element Plus UI Framework Integration Instructions for GitHub Copilot
6
+
7
+ ## Project Overview
8
+
9
+ Element Plus serves as the primary UI component framework, integrated with Vue.js 3 through a structured plugin system. Element Plus provides robust form components, navigation elements, and interactive UI components that are globally registered and customized through SASS overrides. The integration follows a clear separation between JavaScript component registration and SASS styling, maintaining optimal build performance and consistent theming.
10
+
11
+ Element Plus components are selectively imported and registered globally, with custom styling applied through dedicated SASS override files that layer on top of the built-in Element Plus theme-chalk styles.
12
+
13
+ ## Integration Architecture
14
+
15
+ ### Project Structure Variations
16
+
17
+ **Important**: Build file locations vary by project configuration. Common patterns include:
18
+
19
+ - `resources/` directory
20
+ - `_Build/` directory (most common)
21
+ - Custom directory structures as defined in project setup
22
+
23
+ Always check your project's specific directory structure. File paths in these examples use `[build-dir]` placeholder but should be adapted to your project's build directory.
24
+
25
+ ### JavaScript Registration (`[build-dir]/js/libs/elements.js`)
26
+
27
+ Element Plus components are registered through a dedicated Vue plugin:
28
+
29
+ ```javascript
30
+ import {
31
+ ElCheckbox,
32
+ ElSelect,
33
+ ElOption,
34
+ ElInfiniteScroll,
35
+ ElPopover,
36
+ ElMenu,
37
+ ElSubMenu,
38
+ ElMenuItem,
39
+ } from "element-plus";
40
+
41
+ export default {
42
+ install(Vue) {
43
+ Vue.component(ElCheckbox.name, ElCheckbox);
44
+ Vue.component(ElSelect.name, ElSelect);
45
+ Vue.component(ElOption.name, ElOption);
46
+ Vue.component(ElPopover.name, ElPopover);
47
+ Vue.component(ElMenu.name, ElMenu);
48
+ Vue.component(ElSubMenu.name, ElSubMenu);
49
+ Vue.component(ElMenuItem.name, ElMenuItem);
50
+
51
+ Vue.use(ElInfiniteScroll);
52
+ },
53
+ };
54
+ ```
55
+
56
+ ### Plugin Integration (`[build-dir]/js/script.js`)
57
+
58
+ The Element Plus plugin is registered in the main Vue application:
59
+
60
+ ```javascript
61
+ import elements from "./libs/elements";
62
+
63
+ const vue = createApp({
64
+ // App configuration
65
+ });
66
+
67
+ vue.use(elements);
68
+ vue.mount("#app");
69
+ ```
70
+
71
+ ### SASS Integration (`[build-dir]/sass/vendor.scss`)
72
+
73
+ Element Plus styles are imported selectively in the vendor SASS file:
74
+
75
+ ```scss
76
+ /* purgecss start ignore */
77
+ @import "element-plus/theme-chalk/base";
78
+
79
+ @import "element-plus/theme-chalk/el-input";
80
+ @import "element-plus/theme-chalk/el-tag";
81
+ @import "element-plus/theme-chalk/el-checkbox";
82
+ @import "element-plus/theme-chalk/el-scrollbar";
83
+ @import "element-plus/theme-chalk/el-select";
84
+ @import "element-plus/theme-chalk/el-option";
85
+ @import "element-plus/theme-chalk/el-option-group";
86
+ @import "element-plus/theme-chalk/el-popover";
87
+ @import "element-plus/theme-chalk/el-popper";
88
+ @import "element-plus/theme-chalk/el-dropdown";
89
+ @import "element-plus/theme-chalk/el-menu";
90
+ /* purgecss end ignore */
91
+ ```
92
+
93
+ ## Component Selection and Documentation
94
+
95
+ ### Element Plus Documentation
96
+
97
+ Reference the official Element Plus documentation for component options and slots:
98
+
99
+ - **Documentation**: https://element-plus.org/en-US/component/overview.html
100
+ - **Version**: Always assume latest version
101
+ - **Evaluation Process**: Browse the component library and select components based on project needs
102
+
103
+ ### Common Components by Category
104
+
105
+ #### Form Components (Most Frequently Used):
106
+
107
+ - `ElCheckbox` - Checkbox inputs with group support
108
+ - `ElSelect` / `ElOption` - Dropdown select components
109
+ - `ElInput` - Text input fields
110
+ - `ElTag` - Tag/chip components for selections
111
+
112
+ #### Navigation Components:
113
+
114
+ - `ElMenu` / `ElSubMenu` / `ElMenuItem` - Navigation menu systems
115
+ - `ElDropdown` - Dropdown menu actions
116
+
117
+ #### Overlay Components:
118
+
119
+ - `ElPopover` - Tooltip and popover overlays
120
+ - `ElPopper` - Positioning engine for overlays
121
+
122
+ #### Utility Components:
123
+
124
+ - `ElScrollbar` - Custom scrollbar styling
125
+ - `ElInfiniteScroll` - Infinite scroll directive
126
+
127
+ ## Adding New Element Plus Components
128
+
129
+ ### Step 1: JavaScript Registration
130
+
131
+ Add the component import and registration to `[build-dir]/js/libs/elements.js`:
132
+
133
+ ```javascript
134
+ import {
135
+ // Existing imports
136
+ ElButton, // New component
137
+ } from "element-plus";
138
+
139
+ export default {
140
+ install(Vue) {
141
+ // Existing registrations
142
+ Vue.component(ElButton.name, ElButton); // New registration
143
+ },
144
+ };
145
+ ```
146
+
147
+ ### Step 2: SASS Import
148
+
149
+ Add the corresponding SASS import to `[build-dir]/sass/vendor.scss`:
150
+
151
+ ```scss
152
+ /* purgecss start ignore */
153
+ @import "element-plus/theme-chalk/base";
154
+
155
+ // Existing imports
156
+ @import "element-plus/theme-chalk/el-button"; // New import
157
+
158
+ /* purgecss end ignore */
159
+ ```
160
+
161
+ ### Step 3: Custom Styling (Optional)
162
+
163
+ For component-specific styling overrides, add styles to `[build-dir]/sass/components/_element.scss` or create individual override files for larger projects:
164
+
165
+ **Single File Approach:**
166
+
167
+ ```scss
168
+ // [build-dir]/sass/components/_element.scss
169
+ .el-button {
170
+ border-radius: 4px;
171
+ font-weight: $fontWeightMedium;
172
+
173
+ &--primary {
174
+ background-color: $color1;
175
+ border-color: $color1;
176
+ }
177
+ }
178
+ ```
179
+
180
+ **Individual File Approach (Larger Projects):**
181
+
182
+ ```scss
183
+ // [build-dir]/sass/components/_el-button.scss
184
+ .el-button {
185
+ border-radius: 4px;
186
+ font-weight: $fontWeightMedium;
187
+
188
+ &--primary {
189
+ background-color: $color1;
190
+ border-color: $color1;
191
+ }
192
+
193
+ &--secondary {
194
+ background-color: transparent;
195
+ border-color: $color2;
196
+ color: $color2;
197
+ }
198
+ }
199
+ ```
200
+
201
+ Then import in `[build-dir]/sass/general.scss`:
202
+
203
+ ```scss
204
+ @import "./components/_el-button.scss";
205
+ ```
206
+
207
+ ## Vue Template Usage
208
+
209
+ ### Basic Component Usage
210
+
211
+ Once registered globally, Element Plus components are available in all Vue templates:
212
+
213
+ ```vue
214
+ <template>
215
+ <div class="form-container">
216
+ <!-- Select dropdown -->
217
+ <el-select
218
+ v-model="selectedValue"
219
+ placeholder="Select an option"
220
+ class="form__select"
221
+ >
222
+ <el-option
223
+ v-for="item in options"
224
+ :key="item.value"
225
+ :label="item.label"
226
+ :value="item.value"
227
+ />
228
+ </el-select>
229
+
230
+ <!-- Checkbox -->
231
+ <el-checkbox v-model="isChecked" class="form__checkbox">
232
+ Enable notifications
233
+ </el-checkbox>
234
+
235
+ <!-- Menu navigation -->
236
+ <el-menu mode="horizontal" class="navigation">
237
+ <el-menu-item index="1">Dashboard</el-menu-item>
238
+ <el-sub-menu index="2">
239
+ <template #title>Reports</template>
240
+ <el-menu-item index="2-1">Analytics</el-menu-item>
241
+ <el-menu-item index="2-2">Performance</el-menu-item>
242
+ </el-sub-menu>
243
+ </el-menu>
244
+
245
+ <!-- Popover tooltip -->
246
+ <el-popover
247
+ placement="top"
248
+ title="Help Information"
249
+ :width="200"
250
+ trigger="hover"
251
+ content="This provides additional context about the feature."
252
+ >
253
+ <template #reference>
254
+ <el-button>Hover for info</el-button>
255
+ </template>
256
+ </el-popover>
257
+ </div>
258
+ </template>
259
+ ```
260
+
261
+ ### Custom Class Integration
262
+
263
+ Combine Element Plus components with project BEM classes and lab-ui utilities:
264
+
265
+ ```vue
266
+ <template>
267
+ <div class="user-form user-form--compact">
268
+ <div class="user-form__field">
269
+ <el-select
270
+ v-model="user.role"
271
+ class="user-form__select form__group--seamless"
272
+ placeholder="Select role"
273
+ >
274
+ <el-option label="Admin" value="admin" />
275
+ <el-option label="User" value="user" />
276
+ </el-select>
277
+ </div>
278
+
279
+ <div class="user-form__actions flex justify-end mt-2">
280
+ <el-button
281
+ type="primary"
282
+ class="user-form__submit"
283
+ @click="handleSubmit"
284
+ >
285
+ Save User
286
+ </el-button>
287
+ </div>
288
+ </div>
289
+ </template>
290
+ ```
291
+
292
+ ## Styling Integration
293
+
294
+ ### Theme Integration
295
+
296
+ Element Plus uses CSS variables for theming, but the typical approach is to layer custom SASS variables and overrides:
297
+
298
+ ```scss
299
+ // Override Element Plus component styles
300
+ .el-input__inner {
301
+ border: none !important;
302
+ box-shadow: none !important;
303
+ min-height: 48px;
304
+ }
305
+
306
+ .el-select {
307
+ .el-input__inner {
308
+ padding: $spacing * 0.5;
309
+ }
310
+ }
311
+
312
+ // Custom form variants using project variables
313
+ .form__group--seamless {
314
+ .el-input__wrapper {
315
+ --el-select-input-focus-border-color: none;
316
+ --el-border-color: none;
317
+ --el-input-border-color: none;
318
+ --el-input-hover-border-color: none;
319
+ --el-select-border-color-hover: none;
320
+ }
321
+
322
+ .el-input__inner {
323
+ padding: 0;
324
+ height: auto !important;
325
+ min-height: auto !important;
326
+ line-height: 1;
327
+ min-width: auto !important;
328
+ width: 95px !important;
329
+ }
330
+ }
331
+ ```
332
+
333
+ ### Color System Integration
334
+
335
+ Use project color variables to maintain consistency with lab-ui:
336
+
337
+ ```scss
338
+ .el-button--primary {
339
+ background-color: $color1;
340
+ border-color: $color1;
341
+
342
+ &:hover {
343
+ background-color: $color2;
344
+ border-color: $color2;
345
+ }
346
+ }
347
+
348
+ .el-popover__title {
349
+ margin-bottom: $spacing * 0.5;
350
+ color: $color8;
351
+ }
352
+
353
+ .el-popover.el-popper {
354
+ color: $fontColor;
355
+ padding: $spacing;
356
+ word-break: break-word;
357
+ text-align: left;
358
+ }
359
+ ```
360
+
361
+ ## Development Workflow
362
+
363
+ ### Adding New Components
364
+
365
+ 1. **Research Component**: Visit https://element-plus.org/en-US/component/overview.html
366
+ 2. **Evaluate Options**: Review props, slots, and events available
367
+ 3. **Register JavaScript**: Add import and registration to `elements.js`
368
+ 4. **Import SASS**: Add theme-chalk import to `vendor.scss`
369
+ 5. **Test Integration**: Verify component works in Vue templates
370
+ 6. **Add Custom Styles**: Create overrides in `_element.scss` or individual files
371
+ 7. **Document Usage**: Update component documentation if needed
372
+
373
+ ### Performance Considerations
374
+
375
+ - **Selective Imports**: Only import components that are actually used
376
+ - **PurgeCSS Protection**: Element Plus styles are wrapped in `/* purgecss start ignore */`
377
+ - **Build Optimization**: Vendor SASS file is cached for faster rebuilds
378
+ - **Tree Shaking**: Unused Element Plus components are automatically excluded
379
+
380
+ ### File Organization
381
+
382
+ **Small to Medium Projects:**
383
+
384
+ - Single override file: `[build-dir]/sass/components/_element.scss`
385
+ - All Element Plus customizations in one location
386
+
387
+ **Large Projects:**
388
+
389
+ - Individual override files: `[build-dir]/sass/components/_el-[component].scss`
390
+ - Better organization and maintainability
391
+ - Easier to track component-specific changes
392
+
393
+ ## Best Practices
394
+
395
+ ### Component Registration
396
+
397
+ - Always register components globally for consistency
398
+ - Use the component's default name (`ElButton.name`)
399
+ - Group related components together in imports
400
+
401
+ ### Styling Approach
402
+
403
+ - Layer custom styles over Element Plus defaults
404
+ - Use project SASS variables for consistent theming
405
+ - Prefer CSS custom properties for dynamic theming
406
+ - Avoid `!important` unless necessary for specificity
407
+
408
+ ### Vue Template Usage
409
+
410
+ - Combine Element Plus classes with project BEM classes
411
+ - Use semantic prop names and clear component structure
412
+ - Leverage slots for flexible content composition
413
+ - Test responsive behavior across breakpoints
414
+
415
+ ### Maintenance
416
+
417
+ - Keep Element Plus version updated to latest
418
+ - Review breaking changes in Element Plus updates
419
+ - Test custom overrides after version updates
420
+ - Document any component-specific customizations
421
+
422
+ Remember: Element Plus provides the foundation UI components, while project-specific styling and behavior are layered on top through the structured SASS architecture and custom component classes.
@@ -0,0 +1,11 @@
1
+ ---
2
+ applyTo: "**/*"
3
+ ---
4
+
5
+ # GitHub Copilot Instructions for this workspace
6
+
7
+ Backends are typically named with the pattern \*-api.
8
+
9
+ Frontends are typically named with the pattern \*-fe. or with no suffix.
10
+
11
+ Ensure you cd into the correct directory before running any terminal commands.
package/cli.js CHANGED
@@ -36,7 +36,15 @@ const args = hideBin(process.argv);
36
36
  // Platform command mapping
37
37
  const platformCommands = {
38
38
  // Base commands available to all platforms
39
- base: ["start", "setup", "connect", "execute", "nuke", "lint"],
39
+ base: [
40
+ "start",
41
+ "setup",
42
+ "connect",
43
+ "execute",
44
+ "nuke",
45
+ "lint",
46
+ "workspace",
47
+ ],
40
48
 
41
49
  // Web development commands (for frameworks that build frontend assets)
42
50
  webDev: [
@@ -0,0 +1,124 @@
1
+ const _ = require("../globals.js");
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const glob = require("glob");
5
+ const { execSync } = require("child_process");
6
+
7
+ // Helper functions
8
+ const exitWithError = (message) => {
9
+ console.error(`Error: ${message}`);
10
+ process.exit(1);
11
+ };
12
+
13
+ const createSymlink = (sourcePath, targetPath) => {
14
+ if (fs.existsSync(targetPath)) fs.unlinkSync(targetPath);
15
+ fs.symlinkSync(
16
+ path.relative(path.dirname(targetPath), sourcePath),
17
+ targetPath,
18
+ );
19
+ };
20
+
21
+ const generateWorkspaceName = (projects, customName) => {
22
+ if (customName) return `workspace-${customName}`;
23
+
24
+ const firstProject = projects[0];
25
+ const parts = firstProject.split("-");
26
+ const baseName =
27
+ parts.length >= 2 ? parts.slice(0, -1).join("-") : firstProject;
28
+ return `workspace-${baseName}`;
29
+ };
30
+
31
+ module.exports = [
32
+ "workspace <projects..>",
33
+ "creates workspace directory with project symlinks and AI instruction files",
34
+ (yargs) => {
35
+ yargs
36
+ .positional("projects", {
37
+ describe: "project directory names to include in workspace",
38
+ type: "array",
39
+ demandOption: true,
40
+ })
41
+ .option("name", {
42
+ alias: "n",
43
+ describe:
44
+ "custom workspace name (will be prefixed with 'workspace-')",
45
+ type: "string",
46
+ });
47
+ },
48
+ (argv) => {
49
+ const { projects, name } = argv;
50
+ const cwd = process.cwd();
51
+
52
+ // Validation
53
+ if (!projects?.length)
54
+ exitWithError("At least one project name is required");
55
+
56
+ const missingProjects = projects.filter(
57
+ (project) => !fs.existsSync(path.join(cwd, project)),
58
+ );
59
+ if (missingProjects.length)
60
+ exitWithError(
61
+ `Project directories not found: ${missingProjects.join(", ")}`,
62
+ );
63
+
64
+ // Setup workspace
65
+ const workspaceName = generateWorkspaceName(projects, name);
66
+ const workspaceDir = path.join(cwd, workspaceName);
67
+
68
+ if (fs.existsSync(workspaceDir))
69
+ exitWithError(
70
+ `Workspace directory '${workspaceName}' already exists`,
71
+ );
72
+
73
+ fs.mkdirSync(workspaceDir);
74
+ console.log(`Created workspace directory: ${workspaceName}`);
75
+
76
+ // Create project symlinks
77
+ projects.forEach((project) => {
78
+ createSymlink(
79
+ path.join(cwd, project),
80
+ path.join(workspaceDir, project),
81
+ );
82
+ console.log(`Linked project: ${project}`);
83
+ });
84
+
85
+ // Setup AI instructions
86
+ const instructionsDir = path.join(
87
+ workspaceDir,
88
+ ".github",
89
+ "instructions",
90
+ );
91
+ fs.mkdirSync(instructionsDir, { recursive: true });
92
+
93
+ const instructionFiles = projects.flatMap((project) =>
94
+ glob.sync(`${project}/.github/instructions/*.md`, { cwd }),
95
+ );
96
+
97
+ instructionFiles.forEach((filePath) => {
98
+ const fileName = path.basename(filePath);
99
+ createSymlink(
100
+ path.join(cwd, filePath),
101
+ path.join(instructionsDir, fileName),
102
+ );
103
+ });
104
+
105
+ console.log(
106
+ instructionFiles.length
107
+ ? `Linked ${instructionFiles.length} instruction file(s) to workspace`
108
+ : "No AI instruction files found in projects",
109
+ );
110
+
111
+ _.copyAIInstructionsFile("workspace-@", workspaceDir);
112
+ _.copyAIInstructionsFile("mcp-@", workspaceDir);
113
+
114
+ // Open in VS Code
115
+ try {
116
+ execSync("code .", { cwd: workspaceDir, stdio: "ignore" });
117
+ console.log(`\nOpened workspace in VS Code: ${workspaceName}`);
118
+ } catch {
119
+ // Silently ignore VS Code errors
120
+ }
121
+
122
+ console.log(`\nWorkspace setup complete: ${workspaceName}`);
123
+ },
124
+ ];
package/globals.js CHANGED
@@ -215,7 +215,8 @@ if (composer && composer.require && composer.require["laravel/framework"]) {
215
215
  args[0] !== "new" &&
216
216
  args[0] !== "key" &&
217
217
  args[0] !== "dekey" &&
218
- args[0] !== "lint"
218
+ args[0] !== "lint" &&
219
+ args[0] !== "workspace"
219
220
  ) {
220
221
  if (!args.find((d) => d === "--force")) {
221
222
  console.log(
@@ -264,7 +265,8 @@ if (composer && composer.require && composer.require["laravel/framework"]) {
264
265
  args[0] !== "new" &&
265
266
  args[0] !== "key" &&
266
267
  args[0] !== "dekey" &&
267
- args[0] !== "lint"
268
+ args[0] !== "lint" &&
269
+ args[0] !== "workspace"
268
270
  ) {
269
271
  if (!args.find((d) => d === "--force")) {
270
272
  console.log(
@@ -338,7 +340,8 @@ if (process.env.FW_NEXT) {
338
340
  args[0] !== "new" &&
339
341
  args[0] !== "key" &&
340
342
  args[0] !== "dekey" &&
341
- args[0] !== "lint"
343
+ args[0] !== "lint" &&
344
+ args[0] !== "workspace"
342
345
  ) {
343
346
  if (
344
347
  !args.find((d) => d === "--force") &&
@@ -570,9 +573,9 @@ if (!existsSync(path.join(cwd, "stylelint.config.js"))) {
570
573
  );
571
574
  }
572
575
 
573
- function copyAIInstructionsFile(source) {
576
+ function copyAIInstructionsFile(source, projectDir = cwd) {
574
577
  // Create directory if it doesn't exist
575
- const destDir = path.join(cwd, ".github/instructions");
578
+ const destDir = path.join(projectDir, ".github/instructions");
576
579
  if (!existsSync(destDir)) {
577
580
  mkdirSync(destDir, { recursive: true });
578
581
  }
@@ -606,11 +609,12 @@ if (
606
609
  semver.satisfies(semver.coerce(pkg.dependencies["vue"]), "3.x")
607
610
  ) {
608
611
  copyAIInstructionsFile("vue-3");
612
+ copyAIInstructionsFile("ui-framework-3");
609
613
  }
610
614
 
611
615
  if (version) {
612
- copyAIInstructionsFile("sass-*");
613
- copyAIInstructionsFile("lab-env-*");
616
+ copyAIInstructionsFile("sass-@");
617
+ copyAIInstructionsFile("lab-env-@");
614
618
  copyAIInstructionsFile("webdriverio-capture-v9");
615
619
  }
616
620
 
@@ -666,7 +670,8 @@ if (
666
670
  args[0] !== "new" &&
667
671
  args[0] !== "key" &&
668
672
  args[0] !== "dekey" &&
669
- args[0] !== "lint"
673
+ args[0] !== "lint" &&
674
+ args[0] !== "workspace"
670
675
  ) {
671
676
  if (!args.find((d) => d === "--force")) {
672
677
  // Stop here if diagnosis either unset or outdated
@@ -827,4 +832,5 @@ module.exports = {
827
832
  opts,
828
833
  );
829
834
  },
835
+ copyAIInstructionsFile,
830
836
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fishawack/lab-env",
3
- "version": "4.45.0-beta.2",
3
+ "version": "4.45.0-beta.3",
4
4
  "description": "Docker manager for FW",
5
5
  "main": "cli.js",
6
6
  "scripts": {