@industry-theme/file-city-panel 0.3.2 → 0.3.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.
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import type { IntroductionTour, IntroductionTourStep } from '../types/IntroductionTour';
2
+ import type { IntroductionTour, IntroductionTourStep } from '@principal-ai/file-city-builder';
3
3
  import type { TextToSpeechAdapter, TourAudioContext } from '../types/TextToSpeech';
4
4
  export interface TourPlayerProps {
5
5
  /** The introduction tour to play */
@@ -1 +1 @@
1
- {"version":3,"file":"TourPlayer.d.ts","sourceRoot":"","sources":["../../src/components/TourPlayer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAiBxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACxF,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGnF,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,IAAI,EAAE,gBAAgB,CAAC;IAEvB,2CAA2C;IAC3C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,iCAAiC;IACjC,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAEvE,+BAA+B;IAC/B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB,uDAAuD;IACvD,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAE1E,yCAAyC;IACzC,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,qIAAqI;IACrI,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IAE3D,wDAAwD;IACxD,WAAW,CAAC,EAAE,OAAO,CAAC;IAGtB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,mBAAmB,CAAC;IAEjC,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAEpC,yDAAyD;IACzD,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,iEAAiE;IACjE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA4/BhD,CAAC"}
1
+ {"version":3,"file":"TourPlayer.d.ts","sourceRoot":"","sources":["../../src/components/TourPlayer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAiBxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAC9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGnF,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,IAAI,EAAE,gBAAgB,CAAC;IAEvB,2CAA2C;IAC3C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,iCAAiC;IACjC,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAEvE,+BAA+B;IAC/B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB,uDAAuD;IACvD,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAE1E,yCAAyC;IACzC,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,qIAAqI;IACrI,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IAE3D,wDAAwD;IACxD,WAAW,CAAC,EAAE,OAAO,CAAC;IAGtB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,mBAAmB,CAAC;IAEjC,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAEpC,yDAAyD;IACzD,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,iEAAiE;IACjE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA4/BhD,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import type { PanelEventBus } from '@principal-ade/panel-framework-core';
3
- import type { IntroductionTour } from '../types/IntroductionTour';
3
+ import type { IntroductionTour } from '@principal-ai/file-city-builder';
4
4
  export interface TourPlayerIntegrationProps {
5
5
  /** The introduction tour */
6
6
  tour: IntroductionTour;
@@ -1 +1 @@
1
- {"version":3,"file":"TourPlayerIntegration.d.ts","sourceRoot":"","sources":["../../src/components/TourPlayerIntegration.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiC,MAAM,OAAO,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAA8C,MAAM,2BAA2B,CAAC;AAG9G,MAAM,WAAW,0BAA0B;IACzC,4BAA4B;IAC5B,IAAI,EAAE,gBAAgB,CAAC;IAEvB,0DAA0D;IAC1D,MAAM,EAAE,aAAa,CAAC;IAEtB,yCAAyC;IACzC,OAAO,EAAE,OAAO,CAAC;IAEjB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB,gIAAgI;IAChI,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;CAC5D;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CA4LtE,CAAC"}
1
+ {"version":3,"file":"TourPlayerIntegration.d.ts","sourceRoot":"","sources":["../../src/components/TourPlayerIntegration.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiC,MAAM,OAAO,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAA8C,MAAM,iCAAiC,CAAC;AAGpH,MAAM,WAAW,0BAA0B;IACzC,4BAA4B;IAC5B,IAAI,EAAE,gBAAgB,CAAC;IAEvB,0DAA0D;IAC1D,MAAM,EAAE,aAAa,CAAC;IAEtB,yCAAyC;IACzC,OAAO,EAAE,OAAO,CAAC;IAEjB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB,gIAAgI;IAChI,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;CAC5D;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CA4LtE,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { IntroductionTour, IntroductionTourStep } from '../types/IntroductionTour';
1
+ import type { IntroductionTour, IntroductionTourStep } from '@principal-ai/file-city-builder';
2
2
  export interface TourPlayerState {
3
3
  /** Current step index */
4
4
  currentStepIndex: number;
@@ -1 +1 @@
1
- {"version":3,"file":"useTourPlayer.d.ts","sourceRoot":"","sources":["../../src/hooks/useTourPlayer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAExF,MAAM,WAAW,eAAe;IAC9B,yBAAyB;IACzB,gBAAgB,EAAE,MAAM,CAAC;IAEzB,mBAAmB;IACnB,WAAW,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAEzC,8BAA8B;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAElB,+BAA+B;IAC/B,SAAS,EAAE,OAAO,CAAC;IAEnB,gCAAgC;IAChC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAE5B,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,qCAAqC;IACrC,KAAK,EAAE,MAAM,IAAI,CAAC;IAElB,qBAAqB;IACrB,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB,sBAAsB;IACtB,KAAK,EAAE,MAAM,IAAI,CAAC;IAElB,uBAAuB;IACvB,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB,sBAAsB;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAC;IAErB,0BAA0B;IAC1B,YAAY,EAAE,MAAM,IAAI,CAAC;IAEzB,mCAAmC;IACnC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC,qCAAqC;IACrC,YAAY,EAAE,MAAM,IAAI,CAAC;IAEzB,+BAA+B;IAC/B,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,wBAAwB;IACxB,KAAK,EAAE,gBAAgB,CAAC;IAExB,iCAAiC;IACjC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAEnE,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IAExB,gCAAgC;IAChC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB,2BAA2B;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,oBAAoB,GAC5B,CAAC,eAAe,EAAE,iBAAiB,CAAC,CA0GtC"}
1
+ {"version":3,"file":"useTourPlayer.d.ts","sourceRoot":"","sources":["../../src/hooks/useTourPlayer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAE9F,MAAM,WAAW,eAAe;IAC9B,yBAAyB;IACzB,gBAAgB,EAAE,MAAM,CAAC;IAEzB,mBAAmB;IACnB,WAAW,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAEzC,8BAA8B;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAElB,+BAA+B;IAC/B,SAAS,EAAE,OAAO,CAAC;IAEnB,gCAAgC;IAChC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAE5B,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,qCAAqC;IACrC,KAAK,EAAE,MAAM,IAAI,CAAC;IAElB,qBAAqB;IACrB,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB,sBAAsB;IACtB,KAAK,EAAE,MAAM,IAAI,CAAC;IAElB,uBAAuB;IACvB,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB,sBAAsB;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAC;IAErB,0BAA0B;IAC1B,YAAY,EAAE,MAAM,IAAI,CAAC;IAEzB,mCAAmC;IACnC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC,qCAAqC;IACrC,YAAY,EAAE,MAAM,IAAI,CAAC;IAEzB,+BAA+B;IAC/B,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,wBAAwB;IACxB,KAAK,EAAE,gBAAgB,CAAC;IAExB,iCAAiC;IACjC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAEnE,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IAExB,gCAAgC;IAChC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB,2BAA2B;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,oBAAoB,GAC5B,CAAC,eAAe,EAAE,iBAAiB,CAAC,CA0GtC"}
@@ -38567,16 +38567,244 @@ const files = {
38567
38567
  defaultConfig,
38568
38568
  includeUnmatched
38569
38569
  };
38570
+ class TourValidationError extends Error {
38571
+ constructor(message, field, value) {
38572
+ super(message);
38573
+ this.field = field;
38574
+ this.value = value;
38575
+ this.name = "TourValidationError";
38576
+ }
38577
+ }
38578
+ function isValidHexColor(color2) {
38579
+ return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(color2);
38580
+ }
38581
+ function isValidVersion(version2) {
38582
+ return /^\d+\.\d+\.\d+$/.test(version2);
38583
+ }
38584
+ function isValidRelativePath(path) {
38585
+ return !path.startsWith("/");
38586
+ }
38587
+ function isValidKebabCase(id2) {
38588
+ return /^[a-z0-9-]+$/.test(id2);
38589
+ }
38590
+ function validateStep(step, index2) {
38591
+ const errors = [];
38592
+ if (!step.id || typeof step.id !== "string") {
38593
+ errors.push(new TourValidationError(`Step ${index2}: Missing or invalid 'id'`, "id", step.id));
38594
+ } else if (!isValidKebabCase(step.id)) {
38595
+ errors.push(new TourValidationError(`Step ${index2}: 'id' must be kebab-case (lowercase, numbers, hyphens only)`, "id", step.id));
38596
+ }
38597
+ if (!step.title || typeof step.title !== "string") {
38598
+ errors.push(new TourValidationError(`Step ${index2}: Missing or invalid 'title'`, "title", step.title));
38599
+ }
38600
+ if (!step.description || typeof step.description !== "string") {
38601
+ errors.push(new TourValidationError(`Step ${index2}: Missing or invalid 'description'`, "description", step.description));
38602
+ }
38603
+ if (step.estimatedTime !== void 0 && (typeof step.estimatedTime !== "number" || step.estimatedTime < 1)) {
38604
+ errors.push(new TourValidationError(`Step ${index2}: 'estimatedTime' must be a positive number`, "estimatedTime", step.estimatedTime));
38605
+ }
38606
+ if (step.focusDirectory !== void 0) {
38607
+ if (typeof step.focusDirectory !== "string") {
38608
+ errors.push(new TourValidationError(`Step ${index2}: 'focusDirectory' must be a string`, "focusDirectory", step.focusDirectory));
38609
+ } else if (!isValidRelativePath(step.focusDirectory)) {
38610
+ errors.push(new TourValidationError(`Step ${index2}: 'focusDirectory' must be a relative path (no leading slash)`, "focusDirectory", step.focusDirectory));
38611
+ }
38612
+ }
38613
+ if (step.autoAdvance && step.autoAdvanceDelay !== void 0) {
38614
+ if (typeof step.autoAdvanceDelay !== "number" || step.autoAdvanceDelay < 1e3) {
38615
+ errors.push(new TourValidationError(`Step ${index2}: 'autoAdvanceDelay' must be at least 1000ms when auto-advance is enabled`, "autoAdvanceDelay", step.autoAdvanceDelay));
38616
+ }
38617
+ }
38618
+ if (step.highlightLayers) {
38619
+ step.highlightLayers.forEach((layer, layerIndex) => {
38620
+ if (!layer.id || !isValidKebabCase(layer.id)) {
38621
+ errors.push(new TourValidationError(`Step ${index2}, Layer ${layerIndex}: Invalid layer 'id' (must be kebab-case)`, `highlightLayers[${layerIndex}].id`, layer.id));
38622
+ }
38623
+ if (!layer.color || !isValidHexColor(layer.color)) {
38624
+ errors.push(new TourValidationError(`Step ${index2}, Layer ${layerIndex}: Invalid hex color`, `highlightLayers[${layerIndex}].color`, layer.color));
38625
+ }
38626
+ if (layer.opacity !== void 0 && (layer.opacity < 0 || layer.opacity > 1)) {
38627
+ errors.push(new TourValidationError(`Step ${index2}, Layer ${layerIndex}: 'opacity' must be between 0 and 1`, `highlightLayers[${layerIndex}].opacity`, layer.opacity));
38628
+ }
38629
+ if (layer.borderWidth !== void 0 && layer.borderWidth < 0) {
38630
+ errors.push(new TourValidationError(`Step ${index2}, Layer ${layerIndex}: 'borderWidth' must be positive`, `highlightLayers[${layerIndex}].borderWidth`, layer.borderWidth));
38631
+ }
38632
+ if (!layer.items || layer.items.length === 0) {
38633
+ errors.push(new TourValidationError(`Step ${index2}, Layer ${layerIndex}: 'items' array must not be empty`, `highlightLayers[${layerIndex}].items`, layer.items));
38634
+ } else {
38635
+ layer.items.forEach((item, itemIndex) => {
38636
+ if (!item.path || !isValidRelativePath(item.path)) {
38637
+ errors.push(new TourValidationError(`Step ${index2}, Layer ${layerIndex}, Item ${itemIndex}: Invalid relative path`, `highlightLayers[${layerIndex}].items[${itemIndex}].path`, item.path));
38638
+ }
38639
+ if (!item.type || item.type !== "file" && item.type !== "directory") {
38640
+ errors.push(new TourValidationError(`Step ${index2}, Layer ${layerIndex}, Item ${itemIndex}: 'type' must be 'file' or 'directory'`, `highlightLayers[${layerIndex}].items[${itemIndex}].type`, item.type));
38641
+ }
38642
+ });
38643
+ }
38644
+ });
38645
+ }
38646
+ if (step.highlightFiles) {
38647
+ step.highlightFiles.forEach((filePath, fileIndex) => {
38648
+ if (!isValidRelativePath(filePath)) {
38649
+ errors.push(new TourValidationError(`Step ${index2}, highlightFiles[${fileIndex}]: Must be a relative path (no leading slash)`, `highlightFiles[${fileIndex}]`, filePath));
38650
+ }
38651
+ });
38652
+ }
38653
+ if (step.interactiveActions) {
38654
+ step.interactiveActions.forEach((action, actionIndex) => {
38655
+ const needsTarget = ["click-file", "hover-directory", "toggle-layer"].includes(action.type);
38656
+ if (needsTarget && !action.target) {
38657
+ errors.push(new TourValidationError(`Step ${index2}, Action ${actionIndex}: '${action.type}' requires a 'target'`, `interactiveActions[${actionIndex}].target`, action.target));
38658
+ }
38659
+ });
38660
+ }
38661
+ return errors;
38662
+ }
38663
+ function validateTour(tour) {
38664
+ const errors = [];
38665
+ if (!tour.id || typeof tour.id !== "string") {
38666
+ errors.push(new TourValidationError("Missing or invalid 'id'", "id", tour.id));
38667
+ } else if (!isValidKebabCase(tour.id)) {
38668
+ errors.push(new TourValidationError("Tour 'id' must be kebab-case (lowercase, numbers, hyphens only)", "id", tour.id));
38669
+ }
38670
+ if (!tour.title || typeof tour.title !== "string") {
38671
+ errors.push(new TourValidationError("Missing or invalid 'title'", "title", tour.title));
38672
+ }
38673
+ if (!tour.description || typeof tour.description !== "string") {
38674
+ errors.push(new TourValidationError("Missing or invalid 'description'", "description", tour.description));
38675
+ }
38676
+ if (!tour.version || !isValidVersion(tour.version)) {
38677
+ errors.push(new TourValidationError("Invalid 'version' - must be semantic version (e.g., '1.0.0')", "version", tour.version));
38678
+ }
38679
+ if (!tour.steps || !Array.isArray(tour.steps) || tour.steps.length === 0) {
38680
+ errors.push(new TourValidationError("'steps' array must contain at least one step", "steps", tour.steps));
38681
+ } else {
38682
+ tour.steps.forEach((step, index2) => {
38683
+ const stepErrors = validateStep(step, index2);
38684
+ errors.push(...stepErrors);
38685
+ });
38686
+ const stepIds = /* @__PURE__ */ new Set();
38687
+ tour.steps.forEach((step, index2) => {
38688
+ if (stepIds.has(step.id)) {
38689
+ errors.push(new TourValidationError(`Duplicate step ID '${step.id}' at index ${index2}`, "steps", step.id));
38690
+ }
38691
+ stepIds.add(step.id);
38692
+ });
38693
+ }
38694
+ if (tour.audience !== void 0 && typeof tour.audience !== "string") {
38695
+ errors.push(new TourValidationError(`Invalid 'audience' - must be a string`, "audience", tour.audience));
38696
+ }
38697
+ return errors;
38698
+ }
38699
+ function parseTour(jsonString) {
38700
+ try {
38701
+ const parsed = JSON.parse(jsonString);
38702
+ if (typeof parsed !== "object" || parsed === null) {
38703
+ return {
38704
+ success: false,
38705
+ errors: [new TourValidationError("Tour JSON must be an object")]
38706
+ };
38707
+ }
38708
+ const errors = validateTour(parsed);
38709
+ if (errors.length > 0) {
38710
+ return {
38711
+ success: false,
38712
+ errors
38713
+ };
38714
+ }
38715
+ return {
38716
+ success: true,
38717
+ tour: parsed
38718
+ };
38719
+ } catch (error) {
38720
+ return {
38721
+ success: false,
38722
+ errors: [
38723
+ new TourValidationError(`Failed to parse JSON: ${error instanceof Error ? error.message : "Unknown error"}`)
38724
+ ]
38725
+ };
38726
+ }
38727
+ }
38728
+ function parseTourOrThrow(jsonString) {
38729
+ var _a;
38730
+ const result = parseTour(jsonString);
38731
+ if (!result.success || !result.tour) {
38732
+ const errorMessages2 = ((_a = result.errors) == null ? void 0 : _a.map((e) => e.message).join("\n")) || "Unknown error";
38733
+ throw new TourValidationError(`Tour validation failed:
38734
+ ${errorMessages2}`);
38735
+ }
38736
+ return result.tour;
38737
+ }
38738
+ function findTourFilePathInFileTree(fileTree) {
38739
+ const tourFiles = fileTree.allFiles.filter((file) => {
38740
+ const path = file.path;
38741
+ return path.endsWith(".tour.json") && !path.includes("/");
38742
+ });
38743
+ tourFiles.sort((a, b) => a.path.localeCompare(b.path));
38744
+ const tourFile = tourFiles[0];
38745
+ return tourFile ? tourFile.path : null;
38746
+ }
38747
+ function findAllTourFilesInFileTree(fileTree) {
38748
+ return fileTree.allFiles.filter((file) => file.path.endsWith(".tour.json")).map((file) => file.path).sort((a, b) => a.localeCompare(b));
38749
+ }
38750
+ async function loadTourFromFileTree(fileTree, readFile) {
38751
+ const tourFilePath = findTourFilePathInFileTree(fileTree);
38752
+ if (!tourFilePath) {
38753
+ return {
38754
+ success: false,
38755
+ errors: [new TourValidationError("No *.tour.json file found in repository root")]
38756
+ };
38757
+ }
38758
+ try {
38759
+ const tourContent = await readFile(tourFilePath);
38760
+ return parseTour(tourContent);
38761
+ } catch (error) {
38762
+ return {
38763
+ success: false,
38764
+ errors: [
38765
+ new TourValidationError(`Failed to read tour file: ${error instanceof Error ? error.message : "Unknown error"}`)
38766
+ ]
38767
+ };
38768
+ }
38769
+ }
38770
+ async function loadAllToursFromFileTree(fileTree, readFile) {
38771
+ const tourPaths = findAllTourFilesInFileTree(fileTree);
38772
+ const results = await Promise.all(tourPaths.map(async (path) => {
38773
+ try {
38774
+ const content = await readFile(path);
38775
+ const result = parseTour(content);
38776
+ return { path, result };
38777
+ } catch (error) {
38778
+ return {
38779
+ path,
38780
+ result: {
38781
+ success: false,
38782
+ errors: [
38783
+ new TourValidationError(`Failed to read tour file: ${error instanceof Error ? error.message : "Unknown error"}`)
38784
+ ]
38785
+ }
38786
+ };
38787
+ }
38788
+ }));
38789
+ return results;
38790
+ }
38570
38791
  const dist = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
38571
38792
  __proto__: null,
38572
38793
  CodeCityBuilderWithGrid,
38573
38794
  CommonSorts,
38574
38795
  GridLayoutManager,
38575
38796
  MultiVersionCityBuilder,
38797
+ TourValidationError,
38576
38798
  buildFileSystemTreeFromFileInfoList,
38577
38799
  buildMultiVersionCity,
38578
38800
  defaultFileColorConfig: files,
38579
- getFilesFromGitHubTree
38801
+ findAllTourFilesInFileTree,
38802
+ findTourFilePathInFileTree,
38803
+ getFilesFromGitHubTree,
38804
+ loadAllToursFromFileTree,
38805
+ loadTourFromFileTree,
38806
+ parseTour,
38807
+ parseTourOrThrow
38580
38808
  }, Symbol.toStringTag, { value: "Module" }));
38581
38809
  const require$$6 = /* @__PURE__ */ getAugmentedNamespace(dist);
38582
38810
  var fileColorOverrides = {};
@@ -53267,272 +53495,6 @@ function getLayersForColorMode(mode, buildings, qualityData, fileColorLayers, gi
53267
53495
  }
53268
53496
  }
53269
53497
  }
53270
- class TourValidationError extends Error {
53271
- constructor(message, field, value) {
53272
- super(message);
53273
- this.field = field;
53274
- this.value = value;
53275
- this.name = "TourValidationError";
53276
- }
53277
- }
53278
- function isValidHexColor(color2) {
53279
- return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(color2);
53280
- }
53281
- function isValidVersion(version2) {
53282
- return /^\d+\.\d+\.\d+$/.test(version2);
53283
- }
53284
- function isValidRelativePath(path) {
53285
- return !path.startsWith("/");
53286
- }
53287
- function validateStep(step, index2) {
53288
- const errors = [];
53289
- if (!step.id || typeof step.id !== "string") {
53290
- errors.push(new TourValidationError(`Step ${index2}: Missing or invalid 'id'`, "id", step.id));
53291
- } else if (!/^[a-z0-9-]+$/.test(step.id)) {
53292
- errors.push(
53293
- new TourValidationError(
53294
- `Step ${index2}: 'id' must be kebab-case (lowercase, numbers, hyphens only)`,
53295
- "id",
53296
- step.id
53297
- )
53298
- );
53299
- }
53300
- if (!step.title || typeof step.title !== "string") {
53301
- errors.push(new TourValidationError(`Step ${index2}: Missing or invalid 'title'`, "title", step.title));
53302
- }
53303
- if (!step.description || typeof step.description !== "string") {
53304
- errors.push(
53305
- new TourValidationError(`Step ${index2}: Missing or invalid 'description'`, "description", step.description)
53306
- );
53307
- }
53308
- if (step.estimatedTime !== void 0 && (typeof step.estimatedTime !== "number" || step.estimatedTime < 1)) {
53309
- errors.push(
53310
- new TourValidationError(
53311
- `Step ${index2}: 'estimatedTime' must be a positive number`,
53312
- "estimatedTime",
53313
- step.estimatedTime
53314
- )
53315
- );
53316
- }
53317
- if (step.focusDirectory !== void 0) {
53318
- if (typeof step.focusDirectory !== "string") {
53319
- errors.push(
53320
- new TourValidationError(
53321
- `Step ${index2}: 'focusDirectory' must be a string`,
53322
- "focusDirectory",
53323
- step.focusDirectory
53324
- )
53325
- );
53326
- } else if (!isValidRelativePath(step.focusDirectory)) {
53327
- errors.push(
53328
- new TourValidationError(
53329
- `Step ${index2}: 'focusDirectory' must be a relative path (no leading slash)`,
53330
- "focusDirectory",
53331
- step.focusDirectory
53332
- )
53333
- );
53334
- }
53335
- }
53336
- if (step.autoAdvance && step.autoAdvanceDelay !== void 0) {
53337
- if (typeof step.autoAdvanceDelay !== "number" || step.autoAdvanceDelay < 1e3) {
53338
- errors.push(
53339
- new TourValidationError(
53340
- `Step ${index2}: 'autoAdvanceDelay' must be at least 1000ms when auto-advance is enabled`,
53341
- "autoAdvanceDelay",
53342
- step.autoAdvanceDelay
53343
- )
53344
- );
53345
- }
53346
- }
53347
- if (step.highlightLayers) {
53348
- step.highlightLayers.forEach((layer, layerIndex) => {
53349
- if (!layer.id || !/^[a-z0-9-]+$/.test(layer.id)) {
53350
- errors.push(
53351
- new TourValidationError(
53352
- `Step ${index2}, Layer ${layerIndex}: Invalid layer 'id'`,
53353
- `highlightLayers[${layerIndex}].id`,
53354
- layer.id
53355
- )
53356
- );
53357
- }
53358
- if (!layer.color || !isValidHexColor(layer.color)) {
53359
- errors.push(
53360
- new TourValidationError(
53361
- `Step ${index2}, Layer ${layerIndex}: Invalid hex color`,
53362
- `highlightLayers[${layerIndex}].color`,
53363
- layer.color
53364
- )
53365
- );
53366
- }
53367
- if (layer.opacity !== void 0 && (layer.opacity < 0 || layer.opacity > 1)) {
53368
- errors.push(
53369
- new TourValidationError(
53370
- `Step ${index2}, Layer ${layerIndex}: 'opacity' must be between 0 and 1`,
53371
- `highlightLayers[${layerIndex}].opacity`,
53372
- layer.opacity
53373
- )
53374
- );
53375
- }
53376
- if (layer.borderWidth !== void 0 && layer.borderWidth < 0) {
53377
- errors.push(
53378
- new TourValidationError(
53379
- `Step ${index2}, Layer ${layerIndex}: 'borderWidth' must be positive`,
53380
- `highlightLayers[${layerIndex}].borderWidth`,
53381
- layer.borderWidth
53382
- )
53383
- );
53384
- }
53385
- if (!layer.items || layer.items.length === 0) {
53386
- errors.push(
53387
- new TourValidationError(
53388
- `Step ${index2}, Layer ${layerIndex}: 'items' array must not be empty`,
53389
- `highlightLayers[${layerIndex}].items`,
53390
- layer.items
53391
- )
53392
- );
53393
- } else {
53394
- layer.items.forEach((item, itemIndex) => {
53395
- if (!item.path || !isValidRelativePath(item.path)) {
53396
- errors.push(
53397
- new TourValidationError(
53398
- `Step ${index2}, Layer ${layerIndex}, Item ${itemIndex}: Invalid relative path`,
53399
- `highlightLayers[${layerIndex}].items[${itemIndex}].path`,
53400
- item.path
53401
- )
53402
- );
53403
- }
53404
- if (!item.type || item.type !== "file" && item.type !== "directory") {
53405
- errors.push(
53406
- new TourValidationError(
53407
- `Step ${index2}, Layer ${layerIndex}, Item ${itemIndex}: 'type' must be 'file' or 'directory'`,
53408
- `highlightLayers[${layerIndex}].items[${itemIndex}].type`,
53409
- item.type
53410
- )
53411
- );
53412
- }
53413
- });
53414
- }
53415
- });
53416
- }
53417
- if (step.highlightFiles) {
53418
- step.highlightFiles.forEach((filePath, fileIndex) => {
53419
- if (!isValidRelativePath(filePath)) {
53420
- errors.push(
53421
- new TourValidationError(
53422
- `Step ${index2}, highlightFiles[${fileIndex}]: Must be a relative path (no leading slash)`,
53423
- `highlightFiles[${fileIndex}]`,
53424
- filePath
53425
- )
53426
- );
53427
- }
53428
- });
53429
- }
53430
- if (step.interactiveActions) {
53431
- step.interactiveActions.forEach((action, actionIndex) => {
53432
- const needsTarget = ["click-file", "hover-directory", "toggle-layer"].includes(action.type);
53433
- if (needsTarget && !action.target) {
53434
- errors.push(
53435
- new TourValidationError(
53436
- `Step ${index2}, Action ${actionIndex}: '${action.type}' requires a 'target'`,
53437
- `interactiveActions[${actionIndex}].target`,
53438
- action.target
53439
- )
53440
- );
53441
- }
53442
- });
53443
- }
53444
- return errors;
53445
- }
53446
- function validateTour(tour) {
53447
- const errors = [];
53448
- if (!tour.id || typeof tour.id !== "string") {
53449
- errors.push(new TourValidationError("Missing or invalid 'id'", "id", tour.id));
53450
- } else if (!/^[a-z0-9-]+$/.test(tour.id)) {
53451
- errors.push(
53452
- new TourValidationError("Tour 'id' must be kebab-case (lowercase, numbers, hyphens only)", "id", tour.id)
53453
- );
53454
- }
53455
- if (!tour.title || typeof tour.title !== "string") {
53456
- errors.push(new TourValidationError("Missing or invalid 'title'", "title", tour.title));
53457
- }
53458
- if (!tour.description || typeof tour.description !== "string") {
53459
- errors.push(new TourValidationError("Missing or invalid 'description'", "description", tour.description));
53460
- }
53461
- if (!tour.version || !isValidVersion(tour.version)) {
53462
- errors.push(
53463
- new TourValidationError(
53464
- "Invalid 'version' - must be semantic version (e.g., '1.0.0')",
53465
- "version",
53466
- tour.version
53467
- )
53468
- );
53469
- }
53470
- if (!tour.steps || !Array.isArray(tour.steps) || tour.steps.length === 0) {
53471
- errors.push(new TourValidationError("'steps' array must contain at least one step", "steps", tour.steps));
53472
- } else {
53473
- tour.steps.forEach((step, index2) => {
53474
- const stepErrors = validateStep(step, index2);
53475
- errors.push(...stepErrors);
53476
- });
53477
- const stepIds = /* @__PURE__ */ new Set();
53478
- tour.steps.forEach((step, index2) => {
53479
- if (stepIds.has(step.id)) {
53480
- errors.push(new TourValidationError(`Duplicate step ID '${step.id}' at index ${index2}`, "steps", step.id));
53481
- }
53482
- stepIds.add(step.id);
53483
- });
53484
- }
53485
- if (tour.audience !== void 0 && typeof tour.audience !== "string") {
53486
- errors.push(
53487
- new TourValidationError(
53488
- `Invalid 'audience' - must be a string`,
53489
- "audience",
53490
- tour.audience
53491
- )
53492
- );
53493
- }
53494
- return errors;
53495
- }
53496
- function parseTour(jsonString) {
53497
- try {
53498
- const parsed = JSON.parse(jsonString);
53499
- if (typeof parsed !== "object" || parsed === null) {
53500
- return {
53501
- success: false,
53502
- errors: [new TourValidationError("Tour JSON must be an object")]
53503
- };
53504
- }
53505
- const errors = validateTour(parsed);
53506
- if (errors.length > 0) {
53507
- return {
53508
- success: false,
53509
- errors
53510
- };
53511
- }
53512
- return {
53513
- success: true,
53514
- tour: parsed
53515
- };
53516
- } catch (error) {
53517
- return {
53518
- success: false,
53519
- errors: [
53520
- new TourValidationError(
53521
- `Failed to parse JSON: ${error instanceof Error ? error.message : "Unknown error"}`
53522
- )
53523
- ]
53524
- };
53525
- }
53526
- }
53527
- function findTourFilePathInFileTree(fileTree) {
53528
- const tourFiles = fileTree.allFiles.filter((file) => {
53529
- const path = file.path;
53530
- return path.endsWith(".tour.json") && !path.includes("/");
53531
- });
53532
- tourFiles.sort((a, b) => a.path.localeCompare(b.path));
53533
- const tourFile = tourFiles[0];
53534
- return tourFile ? tourFile.path : null;
53535
- }
53536
53498
  const GIT_STATUS_COLORS = {
53537
53499
  staged: "#22c55e",
53538
53500
  // Green
@@ -54735,7 +54697,7 @@ const CodeCityPanelContent = ({
54735
54697
  }
54736
54698
  )
54737
54699
  ] }) }),
54738
- !showTourPlayer && !headerCompact && tourData && /* @__PURE__ */ jsxs(
54700
+ !showTourPlayer && tourData && /* @__PURE__ */ jsxs(
54739
54701
  "button",
54740
54702
  {
54741
54703
  onClick: () => {