clearotron 0.3.0-beta.3 → 0.3.0-beta.4

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.
@@ -10764,7 +10764,33 @@ var api = {
10764
10764
  ...asString(r["sub"]) ? { sub: asString(r["sub"]) } : {},
10765
10765
  ...asString(r["verifiedOn"]) ? { verifiedOn: asString(r["verifiedOn"]) } : {},
10766
10766
  ...asString(r["by"]) ? { by: asString(r["by"]) } : {},
10767
- steps: asArray(r["steps"]).filter((x) => typeof x === "string" && x !== ""),
10767
+ steps: asArray(r["steps"]).flatMap((x) => {
10768
+ const s = asRecord(x);
10769
+ const text = asString(s["text"]);
10770
+ if (!text) return [];
10771
+ const hint = asString(s["hint"]);
10772
+ const c = s["copy"] == null ? null : asRecord(s["copy"]);
10773
+ let copy;
10774
+ if (c) {
10775
+ const kind = asString(c["kind"]);
10776
+ if (kind === "block" && asString(c["text"])) copy = {
10777
+ kind,
10778
+ text: asString(c["text"])
10779
+ };
10780
+ else if (kind === "secret" && asString(c["label"]) && asString(c["template"]) && asString(c["slot"]) && asString(c["template"]).includes(asString(c["slot"]))) copy = {
10781
+ kind,
10782
+ label: asString(c["label"]),
10783
+ template: asString(c["template"]),
10784
+ slot: asString(c["slot"])
10785
+ };
10786
+ else return [];
10787
+ }
10788
+ return [{
10789
+ text,
10790
+ ...hint ? { hint } : {},
10791
+ ...copy ? { copy } : {}
10792
+ }];
10793
+ }),
10768
10794
  launch: (() => {
10769
10795
  const u = asString(asRecord(r["launch"])["url"]);
10770
10796
  return u && /^https:\/\//.test(u) ? { url: u } : null;
@@ -11149,6 +11175,7 @@ var PATHS = {
11149
11175
  bookmark: "M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2Z",
11150
11176
  sliders: "M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6",
11151
11177
  server: "M20 2H4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2ZM20 14H4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2ZM6 6h.01M6 18h.01",
11178
+ laptop: "M20 16V7a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v9m16 0H4m16 0 1.28 2.55a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45L4 16",
11152
11179
  menu: "M3 12h18M3 6h18M3 18h18",
11153
11180
  x: "M18 6 6 18M6 6l12 12",
11154
11181
  chevron: "m9 18 6-6-6-6",
@@ -11156,6 +11183,7 @@ var PATHS = {
11156
11183
  "chevron-right": "m9 18 6-6-6-6",
11157
11184
  "grip-vertical": "M9 5h.01M15 5h.01M9 12h.01M15 12h.01M9 19h.01M15 19h.01",
11158
11185
  "arrow-right": "M5 12h14M13 6l6 6-6 6",
11186
+ "arrow-left": "M19 12H5M11 18l-6-6 6-6",
11159
11187
  trash: "M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6M10 11v6M14 11v6",
11160
11188
  search: "M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM21 21l-4.3-4.3",
11161
11189
  eye: "M2 12s4-7 10-7 10 7 10 7-4 7-10 7-10-7-10-7ZM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z",
@@ -11487,6 +11515,157 @@ function accessSentence(input) {
11487
11515
  return limits.length ? `${first} They ${limits.join(", and ")}.` : first;
11488
11516
  }
11489
11517
  //#endregion
11518
+ //#region src/shell/permissions.ts
11519
+ function canManage(who) {
11520
+ return who.permissions.manage;
11521
+ }
11522
+ function canRun(who) {
11523
+ return who.permissions.run;
11524
+ }
11525
+ function seesEverything(who) {
11526
+ return who.allAccounts === true;
11527
+ }
11528
+ //#endregion
11529
+ //#region src/shell/CompanyPicker.tsx
11530
+ function CompanyCard({ row, onPick }) {
11531
+ return (0, import_jsx_runtime.jsxs)("button", {
11532
+ type: "button",
11533
+ className: row.generic ? "entry-card pick-generic" : "entry-card",
11534
+ onClick: onPick,
11535
+ style: {
11536
+ flexDirection: "row",
11537
+ alignItems: "center",
11538
+ gap: 16,
11539
+ padding: "15px 17px"
11540
+ },
11541
+ children: [(0, import_jsx_runtime.jsxs)("span", {
11542
+ style: {
11543
+ minWidth: 0,
11544
+ flex: 1,
11545
+ textAlign: "left"
11546
+ },
11547
+ children: [(0, import_jsx_runtime.jsxs)("span", {
11548
+ className: "entry-title",
11549
+ style: {
11550
+ display: "block",
11551
+ marginBottom: 2
11552
+ },
11553
+ children: [row.name, row.generic ? (0, import_jsx_runtime.jsx)("span", {
11554
+ className: "pill",
11555
+ style: {
11556
+ marginLeft: 8,
11557
+ verticalAlign: "middle",
11558
+ background: "transparent",
11559
+ borderColor: "var(--accent-ink)",
11560
+ color: "var(--text-accent)"
11561
+ },
11562
+ children: "Default"
11563
+ }) : null]
11564
+ }), (0, import_jsx_runtime.jsx)("span", {
11565
+ className: "entry-body",
11566
+ style: {
11567
+ display: "block",
11568
+ overflow: "hidden",
11569
+ textOverflow: "ellipsis",
11570
+ whiteSpace: "nowrap"
11571
+ },
11572
+ children: row.line
11573
+ })]
11574
+ }), (0, import_jsx_runtime.jsx)("span", {
11575
+ "aria-hidden": "true",
11576
+ style: {
11577
+ flex: "none",
11578
+ color: "var(--text-faint)",
11579
+ display: "inline-flex"
11580
+ },
11581
+ children: (0, import_jsx_runtime.jsx)(Icon, {
11582
+ name: "arrow-right",
11583
+ size: 17
11584
+ })
11585
+ })]
11586
+ });
11587
+ }
11588
+ function CompanyPicker({ eyebrow, heading, line, keys, orgOf, organisations, companyName, factsFor, onPick, onAdd }) {
11589
+ const { headings, groups } = pickerGroups(keys, orgOf, organisations, companyName, factsFor);
11590
+ return (0, import_jsx_runtime.jsxs)("div", {
11591
+ className: "screen",
11592
+ children: [(0, import_jsx_runtime.jsx)("div", {
11593
+ className: "eyebrow",
11594
+ children: eyebrow
11595
+ }), (0, import_jsx_runtime.jsxs)("div", {
11596
+ className: "measure",
11597
+ children: [(0, import_jsx_runtime.jsxs)("div", {
11598
+ style: {
11599
+ display: "flex",
11600
+ alignItems: "flex-end",
11601
+ gap: 20,
11602
+ margin: "4px 0 18px"
11603
+ },
11604
+ children: [(0, import_jsx_runtime.jsxs)("div", {
11605
+ style: { flex: 1 },
11606
+ children: [(0, import_jsx_runtime.jsx)("h1", {
11607
+ style: {
11608
+ fontSize: 27,
11609
+ margin: "0 0 4px",
11610
+ color: "var(--text-strong)"
11611
+ },
11612
+ children: heading
11613
+ }), (0, import_jsx_runtime.jsx)("p", {
11614
+ style: {
11615
+ margin: 0,
11616
+ color: "var(--text-muted)",
11617
+ fontSize: 14.5
11618
+ },
11619
+ children: line
11620
+ })]
11621
+ }), onAdd ? (0, import_jsx_runtime.jsx)("button", {
11622
+ type: "button",
11623
+ className: "start-pill",
11624
+ onClick: onAdd,
11625
+ style: {
11626
+ flex: "none",
11627
+ borderColor: "var(--accent-ink)",
11628
+ color: "var(--text-accent)",
11629
+ fontWeight: 700
11630
+ },
11631
+ children: "+ New company"
11632
+ }) : null]
11633
+ }), groups.map((g, i) => (0, import_jsx_runtime.jsxs)("div", { children: [headings && g.org ? (0, import_jsx_runtime.jsx)("div", {
11634
+ className: "eyebrow",
11635
+ style: { margin: i === 0 ? "0 0 8px" : "16px 0 8px" },
11636
+ "data-anon": "mark",
11637
+ children: g.org.name
11638
+ }) : null, (0, import_jsx_runtime.jsx)("div", {
11639
+ style: {
11640
+ display: "flex",
11641
+ flexDirection: "column",
11642
+ gap: 10,
11643
+ marginTop: !headings && i > 0 ? 10 : 0
11644
+ },
11645
+ children: g.rows.map((r) => (0, import_jsx_runtime.jsx)(CompanyCard, {
11646
+ row: r,
11647
+ onPick: () => onPick(r.key)
11648
+ }, r.key))
11649
+ })] }, g.org?.key ?? ""))]
11650
+ })]
11651
+ });
11652
+ }
11653
+ function CompanyGate({ ctx, eyebrow, heading, line }) {
11654
+ return (0, import_jsx_runtime.jsx)(CompanyPicker, {
11655
+ eyebrow,
11656
+ heading,
11657
+ line,
11658
+ keys: ctx.ownerKeys,
11659
+ orgOf: ctx.orgOf,
11660
+ organisations: ctx.organisations,
11661
+ companyName: ctx.ownerName,
11662
+ factsFor: ctx.factsFor,
11663
+ onPick: (key) => ctx.setOwner(key),
11664
+ onAdd: canManage(ctx.me) ? () => ctx.go(NEW_COMPANY_PATH) : void 0
11665
+ });
11666
+ }
11667
+ var NEW_COMPANY_PATH = "/portal/brand/new";
11668
+ //#endregion
11490
11669
  //#region src/shell/AppShell.tsx
11491
11670
  var MOBILE = "(max-width: 899px)";
11492
11671
  function useIsMobile() {
@@ -11741,7 +11920,8 @@ function AppShell({ render }) {
11741
11920
  }), (0, import_jsx_runtime.jsx)(BrandOwnerSwitcher, {
11742
11921
  grouped,
11743
11922
  value: ownerInView,
11744
- onChange: setOwnerGuarded
11923
+ onChange: setOwnerGuarded,
11924
+ onAdd: canManage(me) ? () => go(NEW_COMPANY_PATH) : void 0
11745
11925
  })]
11746
11926
  }), (0, import_jsx_runtime.jsx)(NavList, {
11747
11927
  entries: groups.owner,
@@ -11909,14 +12089,15 @@ function AppShell({ render }) {
11909
12089
  ]
11910
12090
  });
11911
12091
  }
11912
- function BrandOwnerSwitcher({ grouped, value, onChange }) {
12092
+ var NEW_COMPANY_OPTION = ":new-company";
12093
+ function BrandOwnerSwitcher({ grouped, value, onChange, onAdd }) {
11913
12094
  const option = (r) => (0, import_jsx_runtime.jsx)("option", {
11914
12095
  value: r.key,
11915
12096
  children: switcherLabel(r)
11916
12097
  }, r.key);
11917
12098
  return (0, import_jsx_runtime.jsxs)("select", {
11918
12099
  value: value ?? "",
11919
- onChange: (e) => onChange(e.target.value || null),
12100
+ onChange: (e) => e.target.value === NEW_COMPANY_OPTION ? onAdd?.() : onChange(e.target.value || null),
11920
12101
  "aria-label": "Company",
11921
12102
  style: {
11922
12103
  width: "100%",
@@ -11929,13 +12110,21 @@ function BrandOwnerSwitcher({ grouped, value, onChange }) {
11929
12110
  fontWeight: 700,
11930
12111
  color: "var(--text-strong)"
11931
12112
  },
11932
- children: [(0, import_jsx_runtime.jsx)("option", {
11933
- value: "",
11934
- children: ALL_OWNERS
11935
- }), grouped.groups.map((g) => grouped.headings && g.org ? (0, import_jsx_runtime.jsx)("optgroup", {
11936
- label: g.org.name,
11937
- children: g.rows.map(option)
11938
- }, g.org.key) : g.rows.map(option))]
12113
+ children: [
12114
+ (0, import_jsx_runtime.jsx)("option", {
12115
+ value: "",
12116
+ children: ALL_OWNERS
12117
+ }),
12118
+ grouped.groups.map((g) => grouped.headings && g.org ? (0, import_jsx_runtime.jsx)("optgroup", {
12119
+ label: g.org.name,
12120
+ children: g.rows.map(option)
12121
+ }, g.org.key) : g.rows.map(option)),
12122
+ onAdd ? (0, import_jsx_runtime.jsx)("option", {
12123
+ value: NEW_COMPANY_OPTION,
12124
+ "data-action": "new-company",
12125
+ children: "+ New company"
12126
+ }) : null
12127
+ ]
11939
12128
  });
11940
12129
  }
11941
12130
  var initials = (email) => {
@@ -12252,17 +12441,6 @@ function moveBefore(order, id, over) {
12252
12441
  return next;
12253
12442
  }
12254
12443
  //#endregion
12255
- //#region src/shell/permissions.ts
12256
- function canManage(who) {
12257
- return who.permissions.manage;
12258
- }
12259
- function canRun(who) {
12260
- return who.permissions.run;
12261
- }
12262
- function seesEverything(who) {
12263
- return who.allAccounts === true;
12264
- }
12265
- //#endregion
12266
12444
  //#region src/screens/Home.tsx
12267
12445
  var TERMINAL$1 = new Set([
12268
12446
  "delivered",
@@ -15944,146 +16122,6 @@ function unsavedChanges({ current, empty, saved, submitted }) {
15944
16122
  return current !== saved;
15945
16123
  }
15946
16124
  //#endregion
15947
- //#region src/shell/CompanyPicker.tsx
15948
- function CompanyCard({ row, onPick }) {
15949
- return (0, import_jsx_runtime.jsxs)("button", {
15950
- type: "button",
15951
- className: row.generic ? "entry-card pick-generic" : "entry-card",
15952
- onClick: onPick,
15953
- style: {
15954
- flexDirection: "row",
15955
- alignItems: "center",
15956
- gap: 16,
15957
- padding: "15px 17px"
15958
- },
15959
- children: [(0, import_jsx_runtime.jsxs)("span", {
15960
- style: {
15961
- minWidth: 0,
15962
- flex: 1,
15963
- textAlign: "left"
15964
- },
15965
- children: [(0, import_jsx_runtime.jsxs)("span", {
15966
- className: "entry-title",
15967
- style: {
15968
- display: "block",
15969
- marginBottom: 2
15970
- },
15971
- children: [row.name, row.generic ? (0, import_jsx_runtime.jsx)("span", {
15972
- className: "pill",
15973
- style: {
15974
- marginLeft: 8,
15975
- verticalAlign: "middle",
15976
- background: "transparent",
15977
- borderColor: "var(--accent-ink)",
15978
- color: "var(--text-accent)"
15979
- },
15980
- children: "Default"
15981
- }) : null]
15982
- }), (0, import_jsx_runtime.jsx)("span", {
15983
- className: "entry-body",
15984
- style: {
15985
- display: "block",
15986
- overflow: "hidden",
15987
- textOverflow: "ellipsis",
15988
- whiteSpace: "nowrap"
15989
- },
15990
- children: row.line
15991
- })]
15992
- }), (0, import_jsx_runtime.jsx)("span", {
15993
- "aria-hidden": "true",
15994
- style: {
15995
- flex: "none",
15996
- color: "var(--text-faint)",
15997
- display: "inline-flex"
15998
- },
15999
- children: (0, import_jsx_runtime.jsx)(Icon, {
16000
- name: "arrow-right",
16001
- size: 17
16002
- })
16003
- })]
16004
- });
16005
- }
16006
- function CompanyPicker({ eyebrow, heading, line, keys, orgOf, organisations, companyName, factsFor, onPick, onAdd }) {
16007
- const { headings, groups } = pickerGroups(keys, orgOf, organisations, companyName, factsFor);
16008
- return (0, import_jsx_runtime.jsxs)("div", {
16009
- className: "screen",
16010
- children: [(0, import_jsx_runtime.jsx)("div", {
16011
- className: "eyebrow",
16012
- children: eyebrow
16013
- }), (0, import_jsx_runtime.jsxs)("div", {
16014
- className: "measure",
16015
- children: [(0, import_jsx_runtime.jsxs)("div", {
16016
- style: {
16017
- display: "flex",
16018
- alignItems: "flex-end",
16019
- gap: 20,
16020
- margin: "4px 0 18px"
16021
- },
16022
- children: [(0, import_jsx_runtime.jsxs)("div", {
16023
- style: { flex: 1 },
16024
- children: [(0, import_jsx_runtime.jsx)("h1", {
16025
- style: {
16026
- fontSize: 27,
16027
- margin: "0 0 4px",
16028
- color: "var(--text-strong)"
16029
- },
16030
- children: heading
16031
- }), (0, import_jsx_runtime.jsx)("p", {
16032
- style: {
16033
- margin: 0,
16034
- color: "var(--text-muted)",
16035
- fontSize: 14.5
16036
- },
16037
- children: line
16038
- })]
16039
- }), onAdd ? (0, import_jsx_runtime.jsx)("button", {
16040
- type: "button",
16041
- className: "start-pill",
16042
- onClick: onAdd,
16043
- style: {
16044
- flex: "none",
16045
- borderColor: "var(--accent-ink)",
16046
- color: "var(--text-accent)",
16047
- fontWeight: 700
16048
- },
16049
- children: "+ New company"
16050
- }) : null]
16051
- }), groups.map((g, i) => (0, import_jsx_runtime.jsxs)("div", { children: [headings && g.org ? (0, import_jsx_runtime.jsx)("div", {
16052
- className: "eyebrow",
16053
- style: { margin: i === 0 ? "0 0 8px" : "16px 0 8px" },
16054
- "data-anon": "mark",
16055
- children: g.org.name
16056
- }) : null, (0, import_jsx_runtime.jsx)("div", {
16057
- style: {
16058
- display: "flex",
16059
- flexDirection: "column",
16060
- gap: 10,
16061
- marginTop: !headings && i > 0 ? 10 : 0
16062
- },
16063
- children: g.rows.map((r) => (0, import_jsx_runtime.jsx)(CompanyCard, {
16064
- row: r,
16065
- onPick: () => onPick(r.key)
16066
- }, r.key))
16067
- })] }, g.org?.key ?? ""))]
16068
- })]
16069
- });
16070
- }
16071
- function CompanyGate({ ctx, eyebrow, heading, line }) {
16072
- return (0, import_jsx_runtime.jsx)(CompanyPicker, {
16073
- eyebrow,
16074
- heading,
16075
- line,
16076
- keys: ctx.ownerKeys,
16077
- orgOf: ctx.orgOf,
16078
- organisations: ctx.organisations,
16079
- companyName: ctx.ownerName,
16080
- factsFor: ctx.factsFor,
16081
- onPick: (key) => ctx.setOwner(key),
16082
- onAdd: canManage(ctx.me) ? () => ctx.go(NEW_COMPANY_PATH) : void 0
16083
- });
16084
- }
16085
- var NEW_COMPANY_PATH = "/portal/brand/new";
16086
- //#endregion
16087
16125
  //#region src/contract/companyCreated.ts
16088
16126
  var pending = null;
16089
16127
  function handOff(created) {
@@ -20031,19 +20069,19 @@ var WHAT_YOU_CAN_DO = [
20031
20069
  "Ask what-if: why a finding was rated as it was, what changes if the goods narrow"
20032
20070
  ];
20033
20071
  var COPIED_MS = 2600;
20072
+ var PLACES = ["disk", "public-http"];
20034
20073
  var WHERE = {
20035
- disk: "On this computer",
20036
- "public-http": "Somewhere else"
20037
- };
20038
- var EITHER = "either";
20039
- var placesOf = (offers) => {
20040
- const named = [...new Set(offers.map((o) => o.route ?? "public-http"))].filter((r) => r !== EITHER);
20041
- return named.length ? named : offers.length ? ["disk"] : [];
20042
- };
20043
- var servesPlace = (o, place) => {
20044
- const r = o.route ?? "public-http";
20045
- return r === place || r === EITHER;
20074
+ disk: {
20075
+ label: "Clearotron is installed on this machine (laptop/desktop)",
20076
+ icon: "laptop"
20077
+ },
20078
+ "public-http": {
20079
+ label: "Clearotron is running elsewhere (e.g. Cloud/Server)",
20080
+ icon: "server"
20081
+ }
20046
20082
  };
20083
+ var HELP_URL = "https://github.com/CordilleraSarl/clearotron/blob/main/mcp-server/CONNECT.md";
20084
+ var HELP_LINK_TEXT = "GitHub MCP Connector Documentation";
20047
20085
  async function copy(text) {
20048
20086
  try {
20049
20087
  await navigator.clipboard.writeText(text);
@@ -20053,77 +20091,121 @@ async function copy(text) {
20053
20091
  }
20054
20092
  }
20055
20093
  var mask = (key) => key.length > 3 ? `${key.slice(0, 3)}••••` : "••••";
20056
- var pasteLine = (offer) => offer.pasteAs ?? `Paste it into ${offer.name}`;
20057
- function Destination({ offer, selected, copied, onPick }) {
20058
- return (0, import_jsx_runtime.jsxs)("button", {
20059
- type: "button",
20060
- className: "ai-dest",
20061
- "data-id": offer.id,
20062
- "data-selected": selected ? "" : void 0,
20063
- "data-copied": copied ? "" : void 0,
20064
- "aria-pressed": selected,
20065
- onClick: onPick,
20066
- children: [(0, import_jsx_runtime.jsxs)("span", {
20067
- className: "ai-dest-name",
20068
- children: [offer.name, offer.sub ? (0, import_jsx_runtime.jsx)("span", {
20069
- className: "ai-dest-sub",
20070
- children: offer.sub
20071
- }) : null]
20072
- }), (0, import_jsx_runtime.jsxs)("span", {
20073
- className: "ai-dest-say",
20074
- children: [(0, import_jsx_runtime.jsx)("span", {
20075
- className: "ai-dest-idle",
20076
- children: pasteLine(offer)
20077
- }), (0, import_jsx_runtime.jsx)("span", {
20078
- className: "ai-dest-done",
20079
- children: "✓ Copied"
20080
- })]
20081
- })]
20082
- });
20083
- }
20084
- function Panel({ offer, landed }) {
20094
+ var fill = (c, value) => c.template.split(c.slot).join(value);
20095
+ function Marked({ text }) {
20096
+ const parts = text.split(/(\*\*[^*]+\*\*|`[^`]+`)/g).filter((p) => p !== "");
20097
+ return (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: parts.map((p, n) => p.length > 4 && p.startsWith("**") && p.endsWith("**") ? (0, import_jsx_runtime.jsx)("b", { children: p.slice(2, -2) }, n) : p.length > 2 && p.startsWith("`") && p.endsWith("`") ? (0, import_jsx_runtime.jsx)("code", {
20098
+ className: "ai-code",
20099
+ children: p.slice(1, -1)
20100
+ }, n) : (0, import_jsx_runtime.jsx)("span", { children: p }, n)) });
20101
+ }
20102
+ var AT_REST = {
20103
+ copiedAt: null,
20104
+ landedAt: null,
20105
+ landed: null,
20106
+ revealedAt: null,
20107
+ revealed: null
20108
+ };
20109
+ function StepsPanel({ offer, measuring = false, state = AT_REST, onBlock, onSecret }) {
20085
20110
  return (0, import_jsx_runtime.jsxs)("div", {
20086
- className: "ai-panel",
20087
- "data-for": offer.id,
20111
+ className: "steps-panel",
20112
+ "data-for": measuring ? void 0 : offer.id,
20088
20113
  children: [
20114
+ (0, import_jsx_runtime.jsx)("div", {
20115
+ className: "eyebrow",
20116
+ children: "Steps for"
20117
+ }),
20089
20118
  (0, import_jsx_runtime.jsx)("h3", {
20090
- className: "ai-panel-head",
20091
- children: pasteLine(offer)
20119
+ className: "steps-name",
20120
+ children: offer.name
20092
20121
  }),
20093
- landed ? (0, import_jsx_runtime.jsxs)("p", {
20094
- className: "ai-panel-landed",
20095
- children: ["On your clipboard now: ", (0, import_jsx_runtime.jsx)("code", {
20096
- className: "mono",
20097
- children: landed
20098
- })]
20099
- }) : null,
20100
- offer.steps.length ? (0, import_jsx_runtime.jsx)("ol", {
20101
- className: "ai-panel-steps",
20102
- children: offer.steps.map((line) => (0, import_jsx_runtime.jsx)("li", { children: line }, line))
20103
- }) : null,
20104
- offer.verifiedOn ? (0, import_jsx_runtime.jsxs)("p", {
20105
- className: "ai-panel-stamp",
20106
- children: [
20107
- "✓ Checked ",
20108
- offer.verifiedOn,
20109
- " — these are the taps this app actually has today."
20110
- ]
20111
- }) : (0, import_jsx_runtime.jsx)("p", {
20112
- className: "ai-panel-stamp ai-panel-stamp-none",
20113
- children: "These steps name no button we have not opened ourselves. Your app may word them differently."
20122
+ (0, import_jsx_runtime.jsx)("ol", {
20123
+ className: "steps",
20124
+ children: offer.steps.map((s, i) => {
20125
+ const c = s.copy;
20126
+ const flashed = !measuring && state.copiedAt === i;
20127
+ const landed = measuring && c?.kind === "secret" ? fill(c, mask("")) : state.landedAt === i ? state.landed : null;
20128
+ return (0, import_jsx_runtime.jsx)("li", { children: (0, import_jsx_runtime.jsxs)("div", { children: [
20129
+ (0, import_jsx_runtime.jsx)("div", {
20130
+ className: "step-text",
20131
+ children: (0, import_jsx_runtime.jsx)(Marked, { text: s.text })
20132
+ }),
20133
+ c?.kind === "block" ? (0, import_jsx_runtime.jsxs)("div", {
20134
+ className: "codeblock",
20135
+ children: [(0, import_jsx_runtime.jsx)("pre", { children: c.text }), (0, import_jsx_runtime.jsx)("button", {
20136
+ type: "button",
20137
+ className: `btn-ghost${flashed ? " is-copied" : ""}`,
20138
+ "data-step": i,
20139
+ tabIndex: measuring ? -1 : void 0,
20140
+ onClick: () => onBlock?.(i, c.text),
20141
+ children: flashed ? "✓ Copied" : "Copy"
20142
+ })]
20143
+ }) : null,
20144
+ c?.kind === "secret" ? (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
20145
+ (0, import_jsx_runtime.jsx)("div", {
20146
+ className: "secret-btn",
20147
+ children: (0, import_jsx_runtime.jsx)("button", {
20148
+ type: "button",
20149
+ className: `btn-primary${flashed ? " is-copied" : ""}`,
20150
+ "data-step": i,
20151
+ tabIndex: measuring ? -1 : void 0,
20152
+ onClick: () => onSecret?.(i, c),
20153
+ children: flashed ? (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [(0, import_jsx_runtime.jsx)(Icon, {
20154
+ name: "check",
20155
+ size: 15
20156
+ }), "Copied"] }) : landed ? "Copy again" : c.label
20157
+ })
20158
+ }),
20159
+ landed ? (0, import_jsx_runtime.jsxs)("div", {
20160
+ className: "landed",
20161
+ children: [(0, import_jsx_runtime.jsx)("div", {
20162
+ className: "landed-label",
20163
+ children: "On your clipboard now"
20164
+ }), landed.split("\n").map((line, n) => (0, import_jsx_runtime.jsx)("div", {
20165
+ className: "mono",
20166
+ children: line
20167
+ }, n))]
20168
+ }) : null,
20169
+ !measuring && state.revealedAt === i && state.revealed ? (0, import_jsx_runtime.jsxs)("div", {
20170
+ className: "landed",
20171
+ children: [(0, import_jsx_runtime.jsx)("p", {
20172
+ className: "ai-said",
20173
+ children: "Your browser would not let us copy it. Copy this by hand — it is yours, and it will not be shown again."
20174
+ }), (0, import_jsx_runtime.jsx)("pre", {
20175
+ className: "ai-pre",
20176
+ children: state.revealed
20177
+ })]
20178
+ }) : null
20179
+ ] }) : null,
20180
+ s.hint ? (0, import_jsx_runtime.jsx)("div", {
20181
+ className: "step-hint",
20182
+ children: (0, import_jsx_runtime.jsx)(Marked, { text: s.hint })
20183
+ }) : null
20184
+ ] }) }, i);
20185
+ })
20114
20186
  })
20115
20187
  ]
20116
20188
  });
20117
20189
  }
20190
+ function NotOnline() {
20191
+ return (0, import_jsx_runtime.jsxs)("div", {
20192
+ className: "notice ai-none",
20193
+ children: [(0, import_jsx_runtime.jsx)("strong", { children: "Not available on this installation yet" }), (0, import_jsx_runtime.jsx)("p", { children: "Your AI app reaches Clearotron over the internet, and this installation isn’t on the internet yet. Whoever installed it can put it online — it takes about a minute and needs no account." })]
20194
+ });
20195
+ }
20118
20196
  function UseYourAI({ ctx }) {
20119
20197
  const [access, setAccess] = (0, import_react.useState)(null);
20120
- const [route, setRoute] = (0, import_react.useState)(null);
20198
+ const [place, setPlace] = (0, import_react.useState)(null);
20121
20199
  const [picked, setPicked] = (0, import_react.useState)(null);
20200
+ const [copiedAt, setCopiedAt] = (0, import_react.useState)(null);
20201
+ const [landedAt, setLandedAt] = (0, import_react.useState)(null);
20122
20202
  const [landed, setLanded] = (0, import_react.useState)(null);
20123
- const [copiedId, setCopiedId] = (0, import_react.useState)(null);
20124
20203
  const [failed, setFailed] = (0, import_react.useState)(null);
20204
+ const [revealedAt, setRevealedAt] = (0, import_react.useState)(null);
20125
20205
  const [revealed, setRevealed] = (0, import_react.useState)(null);
20206
+ const [slotHeight, setSlotHeight] = (0, import_react.useState)(0);
20126
20207
  const timer = (0, import_react.useRef)(null);
20208
+ const probe = (0, import_react.useRef)(null);
20127
20209
  (0, import_react.useEffect)(() => {
20128
20210
  api.mcpAccess().then((r) => {
20129
20211
  if (r.kind === "ok") setAccess(r.value);
@@ -20133,43 +20215,66 @@ function UseYourAI({ ctx }) {
20133
20215
  if (timer.current) window.clearTimeout(timer.current);
20134
20216
  }, []);
20135
20217
  const served = (access?.offers ?? []).filter((o) => o.served);
20136
- const routes = placesOf(served);
20137
- const active = route ?? (routes.includes("disk") ? "disk" : routes[0] ?? null);
20138
- const here = served.filter((o) => active !== null && servesPlace(o, active));
20218
+ const routes = PLACES.filter((p) => served.some((o) => o.route === p));
20219
+ const asks = routes.includes("disk");
20220
+ const active = asks ? place : routes[0] ?? null;
20221
+ const here = served.filter((o) => active !== null && o.route === active);
20139
20222
  const chosen = here.find((o) => o.id === picked) ?? null;
20140
- const press = async (offer) => {
20141
- setPicked(offer.id);
20223
+ (0, import_react.useLayoutEffect)(() => {
20224
+ const el = probe.current;
20225
+ if (!el) return;
20226
+ const measure = () => setSlotHeight(Math.max(0, ...[...el.children].map((c) => c.offsetHeight)));
20227
+ measure();
20228
+ const seen = new ResizeObserver(measure);
20229
+ seen.observe(el);
20230
+ return () => seen.disconnect();
20231
+ }, [active, here.length]);
20232
+ const settle = () => {
20233
+ setCopiedAt(null);
20234
+ setLandedAt(null);
20235
+ setLanded(null);
20236
+ setRevealedAt(null);
20237
+ setRevealed(null);
20238
+ setFailed(null);
20239
+ };
20240
+ const flash = (i) => {
20241
+ setCopiedAt(i);
20242
+ if (timer.current) window.clearTimeout(timer.current);
20243
+ timer.current = window.setTimeout(() => setCopiedAt(null), COPIED_MS);
20244
+ };
20245
+ const copyBlock = async (i, text) => {
20246
+ setFailed(null);
20247
+ if (!await copy(text)) {
20248
+ setFailed("Your browser would not let us copy it. Select the text above and copy it by hand.");
20249
+ return;
20250
+ }
20251
+ flash(i);
20252
+ };
20253
+ const press = async (i, c) => {
20142
20254
  setFailed(null);
20255
+ setRevealedAt(null);
20143
20256
  setRevealed(null);
20144
- setLanded(null);
20145
- if (offer.command) {
20146
- if (!await copy(offer.command)) {
20147
- setFailed("Your browser would not let us copy it. The steps below still apply.");
20148
- return;
20149
- }
20150
- setLanded(offer.command);
20151
- } else {
20152
- const r = await api.connectKey();
20153
- if (r.kind !== "ok") {
20154
- setFailed("We could not set this up just now. Try again, or ask us.");
20155
- return;
20156
- }
20157
- if (!await copy(`${r.value.address}\n${r.value.key}`)) {
20158
- setRevealed({
20159
- address: r.value.address,
20160
- key: r.value.key
20161
- });
20162
- return;
20163
- }
20164
- setLanded(`${r.value.address} ${mask(r.value.key)}`);
20257
+ const r = await api.connectKey();
20258
+ if (r.kind !== "ok") {
20259
+ setFailed("We could not set this up just now. Try again, or ask us.");
20260
+ return;
20165
20261
  }
20166
- setCopiedId(offer.id);
20167
- if (timer.current) window.clearTimeout(timer.current);
20168
- timer.current = window.setTimeout(() => setCopiedId(null), COPIED_MS);
20262
+ if (!await copy(fill(c, r.value.key))) {
20263
+ setRevealedAt(i);
20264
+ setRevealed(fill(c, r.value.key));
20265
+ return;
20266
+ }
20267
+ setLandedAt(i);
20268
+ setLanded(fill(c, mask(r.value.key)));
20269
+ flash(i);
20169
20270
  };
20170
20271
  return (0, import_jsx_runtime.jsxs)("div", {
20171
20272
  className: "screen ai-screen",
20172
20273
  children: [
20274
+ (0, import_jsx_runtime.jsx)("div", {
20275
+ className: "eyebrow",
20276
+ children: "Use your own AI"
20277
+ }),
20173
20278
  (0, import_jsx_runtime.jsx)("h1", {
20174
20279
  className: "ai-title",
20175
20280
  children: "Use your own AI"
@@ -20179,136 +20284,156 @@ function UseYourAI({ ctx }) {
20179
20284
  children: "Run and interrogate clearances from the assistant you already use — by voice, by email, or just by asking."
20180
20285
  }),
20181
20286
  (0, import_jsx_runtime.jsxs)("section", {
20182
- className: "ai-can",
20287
+ className: "ctx-card ai-can",
20183
20288
  children: [
20184
20289
  (0, import_jsx_runtime.jsx)("h2", { children: "What you can do" }),
20185
- (0, import_jsx_runtime.jsx)("ul", { children: WHAT_YOU_CAN_DO.map((line) => (0, import_jsx_runtime.jsx)("li", { children: line }, line)) }),
20290
+ (0, import_jsx_runtime.jsx)("ul", {
20291
+ className: "ai-can-list",
20292
+ children: WHAT_YOU_CAN_DO.map((line) => (0, import_jsx_runtime.jsxs)("li", { children: [(0, import_jsx_runtime.jsx)(Icon, {
20293
+ name: "check",
20294
+ size: 15
20295
+ }), line] }, line))
20296
+ }),
20186
20297
  (0, import_jsx_runtime.jsxs)("p", {
20187
20298
  className: "ai-can-foot",
20188
20299
  children: [
20189
- "Every report also has an ",
20300
+ "Use the ",
20190
20301
  (0, import_jsx_runtime.jsx)("strong", { children: "Ask AI" }),
20191
- " button that jumps straight to that run.",
20192
- " ",
20193
- (0, import_jsx_runtime.jsx)("strong", { children: "What you set up here reaches your own clearances and nothing else." })
20302
+ " button on a report."
20194
20303
  ]
20195
20304
  })
20196
20305
  ]
20197
20306
  }),
20198
- (0, import_jsx_runtime.jsx)("h2", {
20199
- className: "ai-connect-head",
20200
- children: "Connect it"
20201
- }),
20202
- !served.length ? (0, import_jsx_runtime.jsxs)("p", {
20203
- className: "ai-none",
20204
- children: [(0, import_jsx_runtime.jsx)("strong", { children: "Not available on this installation yet." }), " Your assistants reach this service over the internet, and this installation is not on the internet. Whoever installed it can put it online — it takes about a minute and needs no account."]
20205
- }) : (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
20206
- routes.length > 1 ? (0, import_jsx_runtime.jsxs)("div", {
20207
- className: "ai-where",
20208
- children: [
20209
- (0, import_jsx_runtime.jsx)("h3", {
20210
- className: "ai-where-q",
20211
- children: "Where does your assistant run?"
20212
- }),
20213
- (0, import_jsx_runtime.jsx)("div", {
20214
- className: "ai-seg",
20215
- role: "group",
20216
- "aria-label": "Where does your assistant run?",
20217
- children: routes.map((r) => (0, import_jsx_runtime.jsx)("button", {
20218
- type: "button",
20219
- className: "ai-seg-btn",
20220
- "data-on": r === active ? "" : void 0,
20221
- "aria-pressed": r === active,
20222
- onClick: () => {
20223
- setRoute(r);
20224
- setPicked(null);
20225
- setLanded(null);
20226
- setRevealed(null);
20227
- setFailed(null);
20228
- },
20229
- children: WHERE[r] ?? WHERE["public-http"]
20230
- }, r))
20231
- }),
20232
- (0, import_jsx_runtime.jsx)("p", {
20233
- className: "ai-where-sub",
20234
- children: "Pick your assistant. We copy the one line it needs — nothing to sign up for, nothing opened up."
20235
- })
20236
- ]
20237
- }) : (0, import_jsx_runtime.jsx)("p", {
20238
- className: "ai-where-sub",
20239
- children: "Pick where you will paste it. One press copies everything your assistant needs."
20240
- }),
20241
- (0, import_jsx_runtime.jsx)("div", {
20242
- className: "ai-dests",
20243
- children: here.map((o) => (0, import_jsx_runtime.jsx)(Destination, {
20244
- offer: o,
20245
- selected: o.id === picked,
20246
- copied: o.id === copiedId,
20247
- onPick: () => void press(o)
20248
- }, o.id))
20249
- }),
20250
- (0, import_jsx_runtime.jsxs)("div", {
20251
- className: "ai-slot",
20252
- "aria-live": "polite",
20253
- children: [failed ? (0, import_jsx_runtime.jsx)("p", {
20254
- className: "ai-said",
20255
- children: failed
20256
- }) : null, revealed ? (0, import_jsx_runtime.jsxs)("div", {
20257
- className: "ai-panel",
20258
- children: [(0, import_jsx_runtime.jsxs)("p", {
20259
- className: "ai-said",
20260
- children: [
20261
- "Your browser would not let us copy it. Paste these two lines into",
20262
- " ",
20263
- chosen?.name ?? "your assistant",
20264
- " — they are yours, and they will not be shown again."
20265
- ]
20266
- }), (0, import_jsx_runtime.jsxs)("pre", {
20267
- className: "ai-pre",
20307
+ (0, import_jsx_runtime.jsxs)("section", {
20308
+ className: "ai-connect",
20309
+ children: [(0, import_jsx_runtime.jsx)("h2", {
20310
+ className: "ai-connect-head",
20311
+ children: "Connect it"
20312
+ }), !served.length ? (0, import_jsx_runtime.jsx)(NotOnline, {}) : (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [asks ? (0, import_jsx_runtime.jsx)("div", {
20313
+ className: "where-grid",
20314
+ role: "group",
20315
+ "aria-label": "Where Clearotron is running",
20316
+ children: PLACES.map((p) => (0, import_jsx_runtime.jsxs)("button", {
20317
+ type: "button",
20318
+ className: "where-card",
20319
+ "data-place": p,
20320
+ "aria-pressed": p === active,
20321
+ onClick: () => {
20322
+ if (p !== active) {
20323
+ setPlace(p);
20324
+ setPicked(null);
20325
+ settle();
20326
+ }
20327
+ },
20328
+ children: [
20329
+ (0, import_jsx_runtime.jsx)("span", {
20330
+ className: p === active ? "radio radio-on" : "radio",
20331
+ "aria-hidden": "true"
20332
+ }),
20333
+ (0, import_jsx_runtime.jsx)(Icon, {
20334
+ name: WHERE[p].icon,
20335
+ size: 19
20336
+ }),
20337
+ WHERE[p].label
20338
+ ]
20339
+ }, p))
20340
+ }) : null, active === null ? null : !here.length ? (0, import_jsx_runtime.jsx)(NotOnline, {}) : (0, import_jsx_runtime.jsxs)("div", {
20341
+ className: "ai-connect-grid",
20342
+ children: [(0, import_jsx_runtime.jsxs)("div", { children: [(0, import_jsx_runtime.jsx)("h3", {
20343
+ className: "section-title",
20344
+ children: "Select AI app"
20345
+ }), (0, import_jsx_runtime.jsx)("div", {
20346
+ className: "ai-apps",
20347
+ children: here.map((o) => (0, import_jsx_runtime.jsxs)("button", {
20348
+ type: "button",
20349
+ className: `pick-row ai-app${o.id === picked ? " pick-row-on" : ""}`,
20350
+ "data-id": o.id,
20351
+ "aria-pressed": o.id === picked,
20352
+ onClick: () => {
20353
+ if (o.id !== picked) {
20354
+ setPicked(o.id);
20355
+ settle();
20356
+ }
20357
+ },
20268
20358
  children: [
20269
- revealed.address,
20270
- "\n",
20271
- revealed.key
20359
+ (0, import_jsx_runtime.jsx)("span", {
20360
+ className: o.id === picked ? "radio radio-on" : "radio",
20361
+ "aria-hidden": "true"
20362
+ }),
20363
+ (0, import_jsx_runtime.jsxs)("span", {
20364
+ className: "ai-app-label",
20365
+ children: [(0, import_jsx_runtime.jsx)("span", {
20366
+ className: "ai-app-name",
20367
+ children: o.name
20368
+ }), o.sub ? (0, import_jsx_runtime.jsx)("span", {
20369
+ className: "ai-app-sub",
20370
+ children: o.sub
20371
+ }) : null]
20372
+ }),
20373
+ (0, import_jsx_runtime.jsx)("span", {
20374
+ className: "ai-app-go",
20375
+ children: (0, import_jsx_runtime.jsx)(Icon, {
20376
+ name: "chevron",
20377
+ size: 16
20378
+ })
20379
+ })
20272
20380
  ]
20273
- })]
20274
- }) : chosen ? (0, import_jsx_runtime.jsx)(Panel, {
20275
- offer: chosen,
20276
- landed
20277
- }) : (0, import_jsx_runtime.jsx)("p", {
20278
- className: "ai-slot-empty",
20279
- children: "Pick your assistant and we will copy what it needs, then show you exactly where it goes."
20381
+ }, o.id))
20382
+ })] }), (0, import_jsx_runtime.jsxs)("div", {
20383
+ className: "ai-slot",
20384
+ "aria-live": "polite",
20385
+ style: slotHeight ? { minHeight: slotHeight } : void 0,
20386
+ children: [
20387
+ chosen ? (0, import_jsx_runtime.jsx)(StepsPanel, {
20388
+ offer: chosen,
20389
+ state: {
20390
+ copiedAt,
20391
+ landedAt,
20392
+ landed,
20393
+ revealedAt,
20394
+ revealed
20395
+ },
20396
+ onBlock: (i, text) => void copyBlock(i, text),
20397
+ onSecret: (i, c) => void press(i, c)
20398
+ }) : (0, import_jsx_runtime.jsx)("div", {
20399
+ className: "steps-panel steps-panel-empty",
20400
+ children: (0, import_jsx_runtime.jsxs)("span", {
20401
+ className: "steps-empty",
20402
+ children: [(0, import_jsx_runtime.jsx)(Icon, {
20403
+ name: "arrow-left",
20404
+ size: 16
20405
+ }), "Select AI to see instructions"]
20406
+ })
20407
+ }),
20408
+ failed ? (0, import_jsx_runtime.jsx)("p", {
20409
+ className: "ai-said ai-failed",
20410
+ children: failed
20411
+ }) : null,
20412
+ (0, import_jsx_runtime.jsx)("div", {
20413
+ className: "ai-probe",
20414
+ ref: probe,
20415
+ "aria-hidden": "true",
20416
+ children: here.map((o) => (0, import_jsx_runtime.jsx)(StepsPanel, {
20417
+ offer: o,
20418
+ measuring: true
20419
+ }, o.id))
20420
+ })
20421
+ ]
20280
20422
  })]
20281
- })
20282
- ] }),
20283
- (0, import_jsx_runtime.jsxs)("details", {
20284
- className: "ai-help",
20285
- children: [(0, import_jsx_runtime.jsx)("summary", { children: "Not connecting, or want to set it up yourself?" }), (0, import_jsx_runtime.jsxs)("div", {
20286
- className: "ai-help-body",
20287
- children: [
20288
- (0, import_jsx_runtime.jsxs)("p", { children: [
20289
- "Press the button for your assistant and it will tell you the next thing to do — usually three taps inside that app’s own settings. If your assistant is not listed, pick",
20290
- (0, import_jsx_runtime.jsx)("strong", { children: " Another agent" }),
20291
- ": it will give you both of the things any assistant can take."
20292
- ] }),
20293
- (0, import_jsx_runtime.jsxs)("p", { children: [
20294
- "Once it is connected, you do not have to learn anything new. Ask it in your own words —",
20295
- (0, import_jsx_runtime.jsx)("em", { children: " “start a knockout for our new drinks name across the US”" }),
20296
- ", or",
20297
- (0, import_jsx_runtime.jsx)("em", { children: " “why did you rate that one high?”" }),
20298
- " — and it will do the same work you would do on these screens."
20299
- ] }),
20300
- (0, import_jsx_runtime.jsx)("p", { children: "If a press does not finish, the most common reason is that your assistant runs somewhere this service cannot be reached from. The team who set this up can tell you in a sentence." }),
20301
- (0, import_jsx_runtime.jsxs)("p", {
20302
- className: "ai-help-doc",
20303
- children: [(0, import_jsx_runtime.jsx)("a", {
20304
- href: "/portal/connect-help",
20305
- target: "_blank",
20306
- rel: "noreferrer",
20307
- children: "The full technical instructions"
20308
- }), " — written for an engineer, or for an assistant reading on your behalf."]
20309
- })
20310
- ]
20311
- })]
20423
+ })] })]
20424
+ }),
20425
+ (0, import_jsx_runtime.jsxs)("div", {
20426
+ className: "notice quiet ai-help",
20427
+ children: [
20428
+ (0, import_jsx_runtime.jsx)("strong", { children: "If it doesn’t connect" }),
20429
+ (0, import_jsx_runtime.jsx)("p", { children: "Point your AI at the full technical setup instructions to diagnose." }),
20430
+ (0, import_jsx_runtime.jsx)("p", { children: (0, import_jsx_runtime.jsxs)("a", {
20431
+ href: HELP_URL,
20432
+ target: "_blank",
20433
+ rel: "noreferrer",
20434
+ children: [HELP_LINK_TEXT, " ↗"]
20435
+ }) })
20436
+ ]
20312
20437
  })
20313
20438
  ]
20314
20439
  });