create-interview-cockpit 0.15.0 → 0.16.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.
@@ -4,6 +4,11 @@ import { parseInfraLabWorkspace } from "../infraLab";
4
4
  import {
5
5
  parseFrontendLabWorkspace,
6
6
  ISOLATED_MODULE_FEDERATION_LAB,
7
+ NEXTJS_MF_PLUGIN_LAB,
8
+ NEXTJS_MF_RUNTIME_LAB,
9
+ NEXTJS_MULTI_ZONES_LAB,
10
+ NEXTJS_MF_RUNTIME_API_LAB,
11
+ RSPACK_SHELL_LAB,
7
12
  } from "../reactLab";
8
13
  import { BROWSER_SECURITY_TEMPLATES } from "../browserSecurityTemplates";
9
14
  import type { ContextFile } from "../types";
@@ -23,6 +28,7 @@ import {
23
28
  Link2Off,
24
29
  Network,
25
30
  Shield,
31
+ PenLine,
26
32
  } from "lucide-react";
27
33
 
28
34
  // ─── Helpers ─────────────────────────────────────────────
@@ -34,6 +40,7 @@ const LAB_ORIGINS = new Set([
34
40
  "react",
35
41
  "nextjs",
36
42
  "module-federation",
43
+ "canvas",
37
44
  ]);
38
45
 
39
46
  function isLabFile(cf: ContextFile) {
@@ -193,6 +200,7 @@ export default function LabsPanel() {
193
200
  openNextLab,
194
201
  openModuleFederationLab,
195
202
  openDeploymentLab,
203
+ openCanvasLab,
196
204
  removeQuestionFile,
197
205
  detachLabFile,
198
206
  attachLabFile,
@@ -406,6 +414,17 @@ export default function LabsPanel() {
406
414
  }
407
415
  };
408
416
 
417
+ const openCanvasFile = async (cf: ContextFile) => {
418
+ try {
419
+ const raw = await fetch(`/api/context-files/${cf.id}/content`)
420
+ .then((r) => r.json())
421
+ .then((d) => d.content as string);
422
+ openCanvasLab(raw, cf.id);
423
+ } catch {
424
+ /* ignore */
425
+ }
426
+ };
427
+
409
428
  // ── Section renderer ─────────────────────────────────────
410
429
 
411
430
  function Section({
@@ -640,12 +659,56 @@ export default function LabsPanel() {
640
659
  onClick: () =>
641
660
  openModuleFederationLab(ISOLATED_MODULE_FEDERATION_LAB),
642
661
  },
662
+ {
663
+ label: "Next.js MF — Plugin (Option A)",
664
+ description:
665
+ "Next.js shell + remote via built-in webpack.container.ModuleFederationPlugin",
666
+ onClick: () => openModuleFederationLab(NEXTJS_MF_PLUGIN_LAB),
667
+ },
668
+ {
669
+ label: "Next.js MF — Runtime Loader (Option B)",
670
+ description:
671
+ "Next.js shell with no plugin — loads remote via plain script injection",
672
+ onClick: () => openModuleFederationLab(NEXTJS_MF_RUNTIME_LAB),
673
+ },
674
+ {
675
+ label: "Next.js — Multi-Zones",
676
+ description:
677
+ "Two independent Next.js apps split by URL path via rewrites — Vercel recommended",
678
+ onClick: () => openModuleFederationLab(NEXTJS_MULTI_ZONES_LAB),
679
+ },
680
+ {
681
+ label: "Next.js — MF Runtime API",
682
+ description:
683
+ "@module-federation/enhanced/runtime inside a 'use client' component — no webpack config",
684
+ onClick: () =>
685
+ openModuleFederationLab(NEXTJS_MF_RUNTIME_API_LAB),
686
+ },
687
+ {
688
+ label: "Rspack Shell — Native MF 2.0",
689
+ description:
690
+ "Rspack as the host with built-in MF support; webpack app as the remote",
691
+ onClick: () => openModuleFederationLab(RSPACK_SHELL_LAB),
692
+ },
643
693
  ]}
644
694
  onOpen={openMFFile}
645
695
  openTitle="Open in Webpack Module Federation Lab"
646
696
  accentClass="text-emerald-200"
647
697
  bgClass="bg-emerald-500/10 border border-emerald-500/20"
648
698
  />
699
+ <Section
700
+ title="Canvas Labs"
701
+ icon={PenLine}
702
+ iconColor="text-orange-400/70"
703
+ origin="canvas"
704
+ emptyText="Save a canvas lab to reopen it here"
705
+ onNewLab={() => openCanvasLab()}
706
+ newLabTitle="Open Canvas Lab"
707
+ onOpen={openCanvasFile}
708
+ openTitle="Open in Canvas Lab"
709
+ accentClass="text-orange-200"
710
+ bgClass="bg-orange-500/10 border border-orange-500/20"
711
+ />
649
712
  </div>
650
713
  ) : (
651
714
  <div className="flex-1 flex items-center justify-center">
@@ -154,6 +154,9 @@ export default function Sidebar() {
154
154
 
155
155
  // Drive subfolder navigator
156
156
  const activeWs = workspaces.find((w) => w.id === activeWorkspaceId);
157
+ const activeWsSortOrder = (activeWs?.questionSortOrder ?? "name") as
158
+ | "name"
159
+ | "createdAt";
157
160
  const isDriveWs =
158
161
  activeWs?.type === "google_drive" && !!activeWs.driveConfig?.folderId;
159
162
  const currentSubFolder = activeWs?.driveConfig?.subFolderId
@@ -804,7 +807,15 @@ export default function Sidebar() {
804
807
  )
805
808
  .map((topic) => {
806
809
  const isExpanded = expandedTopics.includes(topic.id);
807
- const questions = questionsByTopic[topic.id] || [];
810
+ const questions = [...(questionsByTopic[topic.id] || [])].sort(
811
+ (a, b) =>
812
+ activeWsSortOrder === "createdAt"
813
+ ? a.createdAt.localeCompare(b.createdAt)
814
+ : a.title.localeCompare(b.title, undefined, {
815
+ numeric: true,
816
+ sensitivity: "base",
817
+ }),
818
+ );
808
819
 
809
820
  return (
810
821
  <div key={topic.id}>
@@ -27,6 +27,7 @@ export default function WorkspaceSwitcher() {
27
27
  createWorkspace,
28
28
  deleteWorkspace,
29
29
  renameWorkspace,
30
+ patchWorkspace,
30
31
  syncWorkspace,
31
32
  linkDriveFolder,
32
33
  attachDriveFolder,
@@ -814,6 +815,41 @@ export default function WorkspaceSwitcher() {
814
815
  )}
815
816
  </div>
816
817
  )}
818
+
819
+ {/* Question sort order */}
820
+ <div
821
+ className="mt-1 ml-5 flex items-center gap-1"
822
+ onClick={(e) => e.stopPropagation()}
823
+ >
824
+ <span className="text-[10px] text-slate-600">Order:</span>
825
+ <button
826
+ onClick={() =>
827
+ patchWorkspace(ws.id, { questionSortOrder: "name" })
828
+ }
829
+ className={`text-[10px] px-1 rounded transition-colors ${
830
+ (ws.questionSortOrder ?? "name") === "name"
831
+ ? "text-cyan-400"
832
+ : "text-slate-600 hover:text-slate-400"
833
+ }`}
834
+ >
835
+ Name
836
+ </button>
837
+ <span className="text-[10px] text-slate-700">·</span>
838
+ <button
839
+ onClick={() =>
840
+ patchWorkspace(ws.id, {
841
+ questionSortOrder: "createdAt",
842
+ })
843
+ }
844
+ className={`text-[10px] px-1 rounded transition-colors ${
845
+ ws.questionSortOrder === "createdAt"
846
+ ? "text-cyan-400"
847
+ : "text-slate-600 hover:text-slate-400"
848
+ }`}
849
+ >
850
+ Date created
851
+ </button>
852
+ </div>
817
853
  </div>
818
854
  ))}
819
855
  </div>