@schandlergarcia/sf-web-components 1.9.86 → 1.9.88
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/.a4drules/skills/command-center-builder/SKILL.md +14 -6
- package/.a4drules/skills/component-library/SKILL.md +1 -1
- package/.a4drules/skills/component-library/chat-data.md +1 -1
- package/CHANGELOG.md +13 -0
- package/package.json +1 -1
- package/scripts/postinstall.mjs +68 -16
- package/scripts/reset-command-center.sh +44 -4
|
@@ -398,19 +398,27 @@ Define schemas outside component body. Always `required: true` on mandatory fiel
|
|
|
398
398
|
|
|
399
399
|
## AI Chat & Agent
|
|
400
400
|
|
|
401
|
-
Two
|
|
401
|
+
Two pieces for agent integration — a **ChatBar** component and the **`useEvaAgent` hook** (Agent REST API):
|
|
402
402
|
|
|
403
403
|
- **`ChatBar`** — command-palette strip for suggested prompts. Place between the hero map and data panels (full-width, `px-4 pt-4`), NOT in the header. Always provide 3–5 `suggestions`.
|
|
404
|
-
- **`
|
|
404
|
+
- **`useEvaAgent`** — pre-installed hook at `src/hooks/useEvaAgent.ts`. Handles OAuth → session → messages → cleanup via the Agentforce Agent REST API. Config is in `src/config/agentApi.ts`. Vite proxy rules in `vite.config.ts` forward requests.
|
|
405
|
+
|
|
406
|
+
> **Do NOT use `AgentforceConversationClient`** (the iframe widget). We use the Agent REST API via `useEvaAgent`.
|
|
407
|
+
> **Do NOT create wrapper hooks** (useEvaChatAdapter, useDisruptionAlerts, etc.). Use `useEvaAgent` directly.
|
|
408
|
+
> **Do NOT create `.env` or `.env.local` files** — config is hardcoded in `src/config/agentApi.ts`.
|
|
405
409
|
|
|
406
410
|
```tsx
|
|
407
|
-
import
|
|
411
|
+
import useEvaAgent from "@/hooks/useEvaAgent";
|
|
412
|
+
|
|
413
|
+
const { messages, isReady, isSending, connect, sendMessage } = useEvaAgent();
|
|
414
|
+
useEffect(() => { connect(); }, [connect]);
|
|
408
415
|
|
|
409
|
-
|
|
416
|
+
const handleChat = (text: string) => {
|
|
417
|
+
sendMessage(text);
|
|
418
|
+
return { text: `Looking into: "${text}"…` };
|
|
419
|
+
};
|
|
410
420
|
```
|
|
411
421
|
|
|
412
|
-
- Return `components` for structured data — never markdown tables in text
|
|
413
|
-
- Always set height on `ChatPanel`
|
|
414
422
|
- Extract `onSend` handlers to module scope when shared
|
|
415
423
|
|
|
416
424
|
## Tables
|
|
@@ -672,7 +672,7 @@ Dropdown select. Value `"all"` skips filtering.
|
|
|
672
672
|
## Data Utilities
|
|
673
673
|
|
|
674
674
|
### DataModeToggle
|
|
675
|
-
**
|
|
675
|
+
> ⚠️ **Do NOT use in the Engine Command Center dashboard.** The Engine dashboard controls data mode via `ENABLE_SAMPLE_DATA_CACHE` in `src/lib/dataStrategy.ts` — there is no UI toggle. Only use `DataModeToggle` in non-Engine dashboards that explicitly need a manual toggle.
|
|
676
676
|
|
|
677
677
|
```jsx
|
|
678
678
|
<DataModeToggle className="ml-4" />
|
|
@@ -54,7 +54,7 @@ Wraps app. Provides `useDataMode()` → `{ mode, isSample, isLive, toggle, setMo
|
|
|
54
54
|
## Data Utilities
|
|
55
55
|
|
|
56
56
|
### DataModeToggle
|
|
57
|
-
**
|
|
57
|
+
> ⚠️ **Do NOT use in the Engine Command Center dashboard.** The Engine dashboard controls data mode via `ENABLE_SAMPLE_DATA_CACHE` in `src/lib/dataStrategy.ts` — there is no UI toggle. Only use `DataModeToggle` in non-Engine dashboards that explicitly need a manual toggle.
|
|
58
58
|
|
|
59
59
|
```jsx
|
|
60
60
|
<DataModeToggle className="ml-4" />
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.9.88] - 2026-04-07
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Postinstall `.a4drules` copy** — now copies all root-level `.md` files (`webapp-ui.md`, `webapp-data.md`, `validation-requirements.md`, `RULES.md`) and all subdirectories (`skills/`, `features/`, `troubleshooting/`), not just `skills/` and `features/`. Ensures the full build guide reaches consuming projects.
|
|
12
|
+
- **Postinstall vite proxy injection** — when `vite.config.ts` already exists, the postinstall now checks for and injects the Agent API proxy rules (`/sf-oauth`, `/sf-agent`) if they're missing, rather than silently skipping.
|
|
13
|
+
- **Reset script `.a4drules` restore** — the reset script now force-copies every `.md` file from the package's `.a4drules` to the project root, preventing stale stubs from persisting across resets.
|
|
14
|
+
|
|
15
|
+
## [1.9.87] - 2026-04-07
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- **SKILL.md AI Chat section** — replaced `AgentforceConversationClient` instructions with `useEvaAgent` hook usage and explicit prohibitions against iframe widget, wrapper hooks, and .env files.
|
|
19
|
+
- **Component library DataModeToggle docs** — added warning that DataModeToggle must NOT be used in the Engine Command Center dashboard (both SKILL.md and chat-data.md).
|
|
20
|
+
|
|
8
21
|
## [1.9.86] - 2026-04-07
|
|
9
22
|
|
|
10
23
|
### Changed
|
package/package.json
CHANGED
package/scripts/postinstall.mjs
CHANGED
|
@@ -429,7 +429,46 @@ if (fs.existsSync(viteConfigTemplatePath) && !fs.existsSync(viteConfigPath)) {
|
|
|
429
429
|
console.error(` ✗ Failed to install vite.config.ts: ${error.message}`);
|
|
430
430
|
}
|
|
431
431
|
} else if (fs.existsSync(viteConfigPath)) {
|
|
432
|
-
|
|
432
|
+
// Ensure Agent API proxy rules exist even in pre-existing vite.config.ts
|
|
433
|
+
try {
|
|
434
|
+
const existing = fs.readFileSync(viteConfigPath, 'utf-8');
|
|
435
|
+
if (!existing.includes('/sf-oauth') || !existing.includes("api.salesforce.com")) {
|
|
436
|
+
const proxyBlock = `
|
|
437
|
+
// Proxy Agentforce Agent API calls through the dev server to avoid CORS.
|
|
438
|
+
// /sf-oauth/* → Salesforce org (for OAuth client-credentials token)
|
|
439
|
+
// /sf-agent/* → Agent API base (for session + message calls)
|
|
440
|
+
server: {
|
|
441
|
+
proxy: {
|
|
442
|
+
'/sf-oauth': {
|
|
443
|
+
target: 'https://tdx26-keynote-org-1com.my.salesforce.com',
|
|
444
|
+
changeOrigin: true,
|
|
445
|
+
rewrite: (p: string) => p.replace(/^\\/sf-oauth/, ''),
|
|
446
|
+
},
|
|
447
|
+
'/sf-agent': {
|
|
448
|
+
target: 'https://api.salesforce.com',
|
|
449
|
+
changeOrigin: true,
|
|
450
|
+
rewrite: (p: string) => p.replace(/^\\/sf-agent/, ''),
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
},
|
|
454
|
+
`;
|
|
455
|
+
// Insert before the build: { section if it exists
|
|
456
|
+
if (existing.includes('build: {') && !existing.includes('server: {')) {
|
|
457
|
+
const updated = existing.replace(
|
|
458
|
+
/(\s*)(\/\/.*Build|build:\s*\{)/,
|
|
459
|
+
proxyBlock + '\n$1$2'
|
|
460
|
+
);
|
|
461
|
+
if (updated !== existing) {
|
|
462
|
+
fs.writeFileSync(viteConfigPath, updated, 'utf-8');
|
|
463
|
+
console.log(' ✓ Added Agent API proxy rules to existing vite.config.ts');
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
} else {
|
|
467
|
+
console.log(' ℹ vite.config.ts already has proxy rules');
|
|
468
|
+
}
|
|
469
|
+
} catch {
|
|
470
|
+
// Non-critical — proxy can be added manually
|
|
471
|
+
}
|
|
433
472
|
}
|
|
434
473
|
|
|
435
474
|
// Copy dataStrategy.ts template
|
|
@@ -501,35 +540,48 @@ if (fs.existsSync(packageJsonPath)) {
|
|
|
501
540
|
|
|
502
541
|
// Copy .a4drules from package to project root (so AI assistants can discover them)
|
|
503
542
|
const packageA4dRules = path.join(packageRoot, '.a4drules');
|
|
504
|
-
const projectRootA4dRules = path.join(cwd, '../../../../../.a4drules'); // Go up from webapp to project root
|
|
505
543
|
|
|
506
544
|
if (fs.existsSync(packageA4dRules)) {
|
|
507
545
|
console.log('\n📋 Copying AI assistant rules to project root...\n');
|
|
508
546
|
|
|
509
|
-
// Resolve to absolute path
|
|
510
547
|
const projectRootPath = path.resolve(cwd, '../../../../../');
|
|
511
548
|
const targetA4dRules = path.join(projectRootPath, '.a4drules');
|
|
512
549
|
|
|
513
|
-
// Create .a4drules if it doesn't exist
|
|
514
550
|
if (!fs.existsSync(targetA4dRules)) {
|
|
515
551
|
fs.mkdirSync(targetA4dRules, { recursive: true });
|
|
516
552
|
}
|
|
517
553
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
554
|
+
let totalA4dCopied = 0;
|
|
555
|
+
|
|
556
|
+
// Force-copy root-level .md files first
|
|
557
|
+
const rootFiles = fs.readdirSync(packageA4dRules).filter(f => f.endsWith('.md'));
|
|
558
|
+
for (const file of rootFiles) {
|
|
559
|
+
try {
|
|
560
|
+
fs.copyFileSync(path.join(packageA4dRules, file), path.join(targetA4dRules, file));
|
|
561
|
+
totalA4dCopied++;
|
|
562
|
+
} catch (error) {
|
|
563
|
+
console.error(` ✗ Failed to copy ${file}: ${error.message}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
if (rootFiles.length > 0) {
|
|
567
|
+
console.log(` ✓ Copied ${rootFiles.length} root rule files`);
|
|
524
568
|
}
|
|
525
569
|
|
|
526
|
-
// Copy features
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
570
|
+
// Copy all subdirectories (skills, features, troubleshooting)
|
|
571
|
+
const subdirs = fs.readdirSync(packageA4dRules).filter(d => {
|
|
572
|
+
const full = path.join(packageA4dRules, d);
|
|
573
|
+
return fs.statSync(full).isDirectory();
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
for (const subdir of subdirs) {
|
|
577
|
+
const src = path.join(packageA4dRules, subdir);
|
|
578
|
+
const dst = path.join(targetA4dRules, subdir);
|
|
579
|
+
const copied = copyDirectoryRecursive(src, dst);
|
|
580
|
+
totalA4dCopied += copied;
|
|
581
|
+
console.log(` ✓ Copied ${copied} files from ${subdir}/`);
|
|
532
582
|
}
|
|
583
|
+
|
|
584
|
+
console.log(` 📋 Total: ${totalA4dCopied} rule files installed`);
|
|
533
585
|
}
|
|
534
586
|
|
|
535
587
|
// Migrate any dashboards from old location (src/components/pages/) to new location (src/pages/)
|
|
@@ -27,6 +27,7 @@ else
|
|
|
27
27
|
fi
|
|
28
28
|
|
|
29
29
|
cd "$ROOT"
|
|
30
|
+
ROOT_ABS="$(cd "$ROOT" && pwd)"
|
|
30
31
|
|
|
31
32
|
echo ""
|
|
32
33
|
echo "╔════════════════════════════════════════════════╗"
|
|
@@ -799,15 +800,53 @@ else
|
|
|
799
800
|
echo " ⚠ Skipped logo (package logo not found)"
|
|
800
801
|
fi
|
|
801
802
|
|
|
803
|
+
# ── Restore .a4drules to project root ─────────────────────────────────────────
|
|
804
|
+
|
|
805
|
+
echo "→ Restoring AI assistant rules (.a4drules)..."
|
|
806
|
+
|
|
807
|
+
# Detect package .a4drules location
|
|
808
|
+
PACKAGE_A4D=""
|
|
809
|
+
if [ -d "node_modules/@schandlergarcia/sf-web-components/.a4drules" ]; then
|
|
810
|
+
PACKAGE_A4D="node_modules/@schandlergarcia/sf-web-components/.a4drules"
|
|
811
|
+
elif [ -d "$SCRIPT_DIR/../.a4drules" ]; then
|
|
812
|
+
PACKAGE_A4D="$SCRIPT_DIR/../.a4drules"
|
|
813
|
+
fi
|
|
814
|
+
|
|
815
|
+
if [ -n "$PACKAGE_A4D" ]; then
|
|
816
|
+
# Resolve project root (5 levels up from webapp, or 1 level up from package source)
|
|
817
|
+
PROJECT_ROOT=""
|
|
818
|
+
if [[ "$ROOT_ABS" == */force-app/main/default/* ]]; then
|
|
819
|
+
PROJECT_ROOT="${ROOT_ABS%%/force-app/main/default/*}"
|
|
820
|
+
else
|
|
821
|
+
PROJECT_ROOT="$ROOT/.."
|
|
822
|
+
fi
|
|
823
|
+
|
|
824
|
+
if [ -n "$PROJECT_ROOT" ] && [ -d "$PROJECT_ROOT" ]; then
|
|
825
|
+
TARGET_A4D="$PROJECT_ROOT/.a4drules"
|
|
826
|
+
a4d_copied=0
|
|
827
|
+
|
|
828
|
+
# Force-copy every .md file individually (cp -R doesn't always overwrite)
|
|
829
|
+
while IFS= read -r src_file; do
|
|
830
|
+
rel="${src_file#$PACKAGE_A4D/}"
|
|
831
|
+
dst="$TARGET_A4D/$rel"
|
|
832
|
+
mkdir -p "$(dirname "$dst")"
|
|
833
|
+
cp -f "$src_file" "$dst"
|
|
834
|
+
a4d_copied=$((a4d_copied + 1))
|
|
835
|
+
done < <(find "$PACKAGE_A4D" -name "*.md" -type f)
|
|
836
|
+
|
|
837
|
+
echo " ✓ Restored $a4d_copied rule files to $TARGET_A4D"
|
|
838
|
+
fi
|
|
839
|
+
else
|
|
840
|
+
echo " ⚠ Skipped .a4drules (package rules not found)"
|
|
841
|
+
fi
|
|
842
|
+
echo ""
|
|
843
|
+
|
|
802
844
|
# ── Clean Salesforce metadata created during the demo ────────────────────────
|
|
803
845
|
|
|
804
846
|
SFDX_DEFAULT=""
|
|
805
847
|
|
|
806
848
|
# The webapp lives inside force-app/main/default/uiBundles/..., so extract
|
|
807
|
-
# the SFDX default directory from our own path first.
|
|
808
|
-
# searching child directories if the pattern doesn't match (standalone app).
|
|
809
|
-
ROOT_ABS="$(cd "$ROOT" && pwd)"
|
|
810
|
-
|
|
849
|
+
# the SFDX default directory from our own path first.
|
|
811
850
|
if [[ "$ROOT_ABS" == */force-app/main/default/* ]]; then
|
|
812
851
|
SFDX_DEFAULT="${ROOT_ABS%%/force-app/main/default/*}/force-app/main/default"
|
|
813
852
|
fi
|
|
@@ -872,6 +911,7 @@ echo "║ • Agent API config (src/config/agentApi.ts)║"
|
|
|
872
911
|
echo "║ • Data strategy (ENABLE_SAMPLE_DATA_CACHE) ║"
|
|
873
912
|
echo "║ • GraphQL schema + PRD + logo ║"
|
|
874
913
|
echo "║ • Component library + theme providers ║"
|
|
914
|
+
echo "║ • AI assistant rules (.a4drules) ║"
|
|
875
915
|
echo "║ ║"
|
|
876
916
|
echo "║ Cleaned: ║"
|
|
877
917
|
echo "║ • Apex classes (force-app classes/) ║"
|