@rubytech/create-sitedesk-code 0.1.401 → 0.1.404

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.
Files changed (62) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/cloudflare/bin/__tests__/cf-token.test.sh +96 -1
  3. package/payload/platform/plugins/cloudflare/bin/cf-token.sh +24 -8
  4. package/payload/platform/plugins/cloudflare/references/api.md +3 -1
  5. package/payload/platform/plugins/web-designer/skills/web-design/references/placeholders.md +19 -2
  6. package/payload/platform/plugins/web-designer/skills/web-design/templates/index.html +12 -12
  7. package/payload/platform/scripts/check-no-esm-require.mjs +3 -0
  8. package/payload/premium-plugins/sitedesk/BUNDLE.md +6 -4
  9. package/payload/premium-plugins/sitedesk/PLUGIN.md +2 -2
  10. package/payload/premium-plugins/sitedesk/agents/sitedesk--media-producer.md +18 -7
  11. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/.claude-plugin/plugin.json +1 -1
  12. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/PLUGIN.md +30 -26
  13. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/README.md +26 -36
  14. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/commands/make-media.md +2 -2
  15. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/SKILL.md +52 -75
  16. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/SKILL.md +52 -120
  17. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/references/sources.md +17 -52
  18. package/payload/server/public/assets/{AdminLoginScreens-rlk263u9.js → AdminLoginScreens-BzErD9Zo.js} +1 -1
  19. package/payload/server/public/assets/AdminShell-BihejKg_.js +1 -0
  20. package/payload/server/public/assets/{Checkbox-Chz6bfy-.js → Checkbox-Ds004CBE.js} +1 -1
  21. package/payload/server/public/assets/{Transcript-Bsx-bGgY.js → Transcript-CgaMQKgY.js} +1 -1
  22. package/payload/server/public/assets/{admin-BnZH758N.js → admin-BSuRtMws.js} +1 -1
  23. package/payload/server/public/assets/{browser-CIrfyZWe.js → browser-BO2eR_YY.js} +1 -1
  24. package/payload/server/public/assets/{calendar-D6Z8Kmrn.js → calendar-12QuR_oh.js} +1 -1
  25. package/payload/server/public/assets/chat-Qpyg5t65.js +1 -0
  26. package/payload/server/public/assets/chevron-left-DBUeR2CX.js +1 -0
  27. package/payload/server/public/assets/data-Ch5bne41.js +1 -0
  28. package/payload/server/public/assets/{graph-XZJZHzvv.js → graph-CDOJZepc.js} +1 -1
  29. package/payload/server/public/assets/{graph-labels-Cabqmf35.js → graph-labels-6HKBjyXX.js} +1 -1
  30. package/payload/server/public/assets/{maximize-2-DPe-Noig.js → maximize-2-DWmyXX8h.js} +1 -1
  31. package/payload/server/public/assets/{operator-BM_CDyCb.js → operator-D-s7jVnf.js} +1 -1
  32. package/payload/server/public/assets/{page-CMIbXzSg.js → page-BcxrdxYL.js} +1 -1
  33. package/payload/server/public/assets/{page-E7I5sEVt.js → page-rDhp8G9q.js} +1 -1
  34. package/payload/server/public/assets/{public-CrdWCSk2.js → public-zK67N3z2.js} +1 -1
  35. package/payload/server/public/assets/{rotate-ccw-Dwtj0NJ7.js → rotate-ccw-BzBJxqBN.js} +1 -1
  36. package/payload/server/public/assets/useSubAccountSwitcher-B-GuP1jj.css +1 -0
  37. package/payload/server/public/browser.html +4 -4
  38. package/payload/server/public/calendar.html +5 -5
  39. package/payload/server/public/chat.html +9 -9
  40. package/payload/server/public/data.html +8 -8
  41. package/payload/server/public/graph.html +9 -9
  42. package/payload/server/public/index.html +11 -11
  43. package/payload/server/public/operator.html +11 -11
  44. package/payload/server/public/public.html +9 -9
  45. package/payload/server/server.js +32 -32
  46. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/brand-extract/SKILL.md +0 -86
  47. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/make-media/SKILL.md +0 -75
  48. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/make-media/references/intake-and-questions.md +0 -74
  49. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/references/placeholders.md +0 -96
  50. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/references/sections.md +0 -48
  51. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/templates/index.html +0 -1004
  52. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/templates/llms.txt +0 -32
  53. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/references/pii-strip.md +0 -64
  54. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/templates/og.html +0 -92
  55. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/templates/social-posts.md +0 -47
  56. package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/templates/socials.html +0 -87
  57. package/payload/server/public/assets/AdminShell-C5vWTy2n.js +0 -1
  58. package/payload/server/public/assets/chat-h7SytZHx.js +0 -1
  59. package/payload/server/public/assets/chevron-left-DSTeS1_g.js +0 -1
  60. package/payload/server/public/assets/data-CL7CyX1C.js +0 -1
  61. package/payload/server/public/assets/useSubAccountSwitcher-BH-YjP0H.css +0 -1
  62. /package/payload/server/public/assets/{useSubAccountSwitcher-BVCMAnWK.js → useSubAccountSwitcher-Bg0RH3Gk.js} +0 -0
@@ -4,10 +4,10 @@ argument-hint: <images-dir-or-zip> [website-url] [social-links] [notes…]
4
4
  allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, Skill, AskUserQuestion
5
5
  ---
6
6
 
7
- Run the `make-media` skill end-to-end on the inputs below.
7
+ Build the media deliverable end-to-end on the inputs below, orchestrating the steps yourself.
8
8
 
9
9
  Inputs: $ARGUMENTS
10
10
 
11
- Follow the orchestrator's routing: `brand-extract` (website + logo + tokens + company facts) → ask the operator the intake questions for every field still missing → `microsite` (build the single-page site into `<output>/site/`) → `socials` in `microsite` mode (render the Open Graph + social tiles, gallery and captions into `<output>/site/socials/`).
11
+ Route: `web-designer:brand-extract` (website + logo + tokens + company facts) → ask the operator for every field still missing → `microsite` (drives `web-designer:web-design` with the construction framing to build the single-page site into `<output>/site/`) → `socials` in `microsite` mode (renders the Open Graph + social tiles, gallery and captions into `<output>/site/socials/` via `web-designer:socials`).
12
12
 
13
13
  Never invent a fact. Where the website and the operator's notes do not supply a value, ask for it before building. Stop at the finished site + social bundle and report the paths.
@@ -1,7 +1,8 @@
1
1
  ---
2
2
  name: microsite
3
- description: Build a finished, scrollable single-page marketing micro-site for a small business from the placeholder template, the brand pack, and the operator's curated photos and copy. Produces a self-contained site/ directory — index.html (hero rotator, story, services, founder section, testimonial, "by the numbers", FAQ, CTA, footer, back-to-top) plus an llms.txtwith structured data (JSON-LD) for answer engines. Use after brand-extract, or directly when the brand pack and copy already exist. Trigger phrases include "build the microsite", "lay out the landing page", "turn this brand + photos into a one-page site".
3
+ description: Build SiteDesk's construction micro-site by driving the shared web-designer builder with construction framing. Runs web-designer:web-design against the brand pack, the operator's copy and their curated photos, supplying the construction schema, UK geography, and the trade-shaped section labels as inputs. Owns no templatethe single source is web-designer's. Use after brand-extract, or directly when the brand pack and copy already exist. Trigger phrases include "build the microsite", "lay out the landing page", "turn this brand + photos into a one-page site".
4
4
  allowed-tools:
5
+ - Skill
5
6
  - Bash
6
7
  - Read
7
8
  - Write
@@ -12,77 +13,53 @@ allowed-tools:
12
13
 
13
14
  # microsite
14
15
 
15
- Renders the canonical single-page site from `templates/index.html` and
16
- `templates/llms.txt` by substituting every `{{ PLACEHOLDER }}`. The template is
17
- the spec: copy it, fill it, never rebuild it from scratch from this prose.
18
-
19
- ## Canonical template — the only starting point
20
-
21
- `templates/index.html` ships with this skill. It encodes the entire site —
22
- structure, CSS, the JS hero rotator + drawer + sticky sub-nav + back-to-top, the
23
- responsive rules, and the JSON-LD with every business-specific value as a
24
- `{{ TOKEN }}`. Copy it to `<output>/site/index.html` and substitute. Do the same
25
- with `templates/llms.txt`. The full token list and what fills each is in
26
- [`references/placeholders.md`](references/placeholders.md); the section catalogue
27
- and the image-naming convention are in [`references/sections.md`](references/sections.md).
28
-
29
- A run that hand-builds the HTML from this prose, or starts from a previous
30
- client's rendered `index.html`, is a defect it drifts every cycle. Copy the
31
- canonical and substitute.
32
-
33
- ## Substitution contract
34
-
35
- - After substitution, `grep "{{" site/index.html site/llms.txt` returns **zero**
36
- matches. Any surviving `{{ }}` is a bug.
37
- - Palette tokens come from the brand pack's ten values. `ACCENT_TEXT` is the
38
- deepened, legible form of `ACCENT` — not the same value.
39
- - Copy tokens are written in the plain register (the `plainly` skill): no
40
- em-dashes in body text, no inflation vocabulary, no AI-marketing cadence. A
41
- real client testimonial is used **verbatim**; never reword it.
42
- - Never invent a fact to fill a token. If `make-media` did not collect it, go back
43
- and ask. An empty contact detail or a guessed year is a defect.
44
-
45
- ## Images curate by content, name by convention
46
-
47
- The template references images by a fixed convention so the operator can tell
48
- what each slot is from the filename:
49
-
50
- - `hero-01.webp hero-10.webp` — the 10-slide hero rotator (configurable down;
51
- the JS cycles however many `.hero-slide` elements exist).
52
- - `grid-01.webp … grid-12.webp` the photo grids.
53
- - `fullbleed-01.webp` the full-width intermission image.
54
- - `founder-portrait.webp`, `founder-avatar.webp` the person who fronts the firm.
55
- - `feature-project.webp`, `gift.webp` the founder section's feature image and
56
- any thank-you artefact.
57
- - `testimonial.webp` — the finished-result image beside the client quote.
58
- - `logo.png`, `icon.png` from brand-extract.
59
-
60
- Curate by **content, verified by eye**, never by filename. Build a labelled
61
- contact sheet (≤ 2000px) and look: match each photo to its slot. Lead the hero
62
- with the strongest finished "arrive here" shot; put the best finished result in
63
- the testimonial; distribute the rest. A person's photo is founder-only; it never
64
- goes in the rotator as a landscape crop. Rewrite every `alt` to describe the
65
- actual photo — the template's alts are exemplar and must not survive as-is if they
66
- don't match the picture.
67
-
68
- ### The 2000px rule
69
-
70
- Never `Read` an image whose longest edge exceeds 2000px — measure first, preview a
71
- downscaled `/tmp` copy if larger, delete it. Optimise to webp: body tier ~1300px
72
- q82; hero tier source-resolution q88; the founder portrait kept tall.
73
-
74
- ## Verify every page state
75
-
76
- Serve `site/` over a local HTTP server and check, at a 1280px desktop and a 390px
77
- mobile viewport: no horizontal overflow, no broken images, the hero rotates, the
78
- drawer opens with every link, the sticky sub-nav reveals on scroll, the back-to-top
79
- appears and works, and both JSON-LD blocks are valid JSON once substituted. Bottom
80
- -align paired images and equal-height cards where the layout calls for it.
81
-
82
- ## Completion criteria
83
-
84
- - `site/index.html` and `site/llms.txt` exist with zero surviving `{{ }}`.
85
- - Every referenced image resolves; alts describe the real photos.
86
- - Desktop + mobile verified; hero, drawer, sub-nav, back-to-top all work.
87
- - JSON-LD (`GeneralContractor`/`LocalBusiness`/the right `SCHEMA_TYPE`, plus
88
- `FAQPage`) parses and matches the visible copy.
16
+ SiteDesk's construction micro-site is the shared `web-designer` site with
17
+ construction framing supplied as input. This skill owns **no template**: it drives
18
+ `web-designer:web-design`, which holds the one canonical template, substitution
19
+ contract, image convention, and page-state verification. Do not copy or hand-build
20
+ any HTML here.
21
+
22
+ ## Method
23
+
24
+ Run `web-designer:web-design` (after `web-designer:brand-extract` has produced the
25
+ brand pack) against the curated photos and the operator's copy. Fill web-design's
26
+ tokens as it specifies, with these SiteDesk construction values for the framing
27
+ tokens it exposes:
28
+
29
+ **Schema and geography**
30
+ - `SCHEMA_TYPE` `GeneralContractor`, or `HomeAndConstructionBusiness` for a
31
+ broader building firm; a narrower schema.org construction subtype when one fits.
32
+ - `COUNTRY_CODE` — `GB`.
33
+ - `PAGE_LANG` — `en-GB`.
34
+ - `COMPANY_NUMBER_LABEL` — `UK company number`.
35
+
36
+ **Section framing (the seven web-design label tokens)**
37
+ - `NAV_STORY_LABEL` `Our story`
38
+ - `NAV_OFFER_LABEL` `What we build`
39
+ - `NAV_HOW_LABEL` — `Our craft`
40
+ - `OFFER_EYEBROW` `What we build`
41
+ - `HOW_EYEBROW` `Ground to finish`
42
+ - `OFFER_SECTION_ID` `build`
43
+ - `HOW_SECTION_ID` `craft`
44
+
45
+ **Copy framing** — write the offer block (`BUILD_*`) as what the firm builds, and
46
+ the process block (`CRAFT_*`) as how a job runs ground to finish. The founder
47
+ section and its "latest update" cards read in the trade's voice — a real job, a
48
+ site the firm is on never invented. Every copy and photo fact is the operator's;
49
+ a fact you cannot source is a gap you flag, not one you make up.
50
+
51
+ ## Verify and report
52
+
53
+ `web-designer:web-design` owns the completion checks (zero surviving `{{ }}`, every
54
+ image resolves, desktop + mobile render, hero/drawer/sub-nav/back-to-top work, both
55
+ JSON-LD blocks parse). Confirm the built site shows the construction framing — the
56
+ nav reads `Our story` / `What we build` / `Our craft`, and the JSON-LD `@type` is
57
+ the construction schema — then report the built `site/` path and any gaps.
58
+
59
+ ## What this does not do
60
+
61
+ - It ships no template and no placeholder or section reference — those live in
62
+ `web-designer:web-design`.
63
+ - It runs no brand extraction or intake of its own `web-designer:brand-extract`
64
+ produces the brand pack and the pipeline gathers the copy.
65
+ - It does not deploy, and it does not render social tiles that is `socials`.
@@ -1,133 +1,65 @@
1
1
  ---
2
2
  name: socials
3
- description: Render share-ready Open Graph and social tiles plus per-platform captions for a named subject — a brand micro-site, a job's progress, a valuation cycle, or a bespoke topic with a PII gate for any source that can carry client data. Resolves the subject through a source adapter into candidate images and copy, overlays brand typography, screenshots landscape (1200×628), square (1200×1200) and portrait (1080×1350) PNG tiles via the on-device browser, assembles a gallery and a captions file for all five platforms, and emits a redaction report reconciling every source item. For micro-sites it also wires the site's og:image. Never posts — the bundle is for operator review. Trigger phrases include "social tiles for this job", "og images for the site", "Instagram tiles + captions for <subject>", "social collateral for <business>".
3
+ description: Render share-ready Open Graph and social tiles plus captions for a SiteDesk subject — a brand micro-site, a job's progress, a valuation cycle, or a bespoke topic. The micro-site and bespoke subjects delegate straight to web-designer:socials. The job and valuation subjects are SiteDesk-specific: they read the job's progress from carried job state under a strict money boundary, stage a brand-safe brief, and render it through web-designer:socials in bespoke mode. Never posts — the bundle is for operator review. Trigger phrases include "social tiles for this job", "og images for the site", "valuation-cycle tiles for <job>", "social collateral for <business>".
4
4
  allowed-tools:
5
+ - Skill
5
6
  - Bash
6
7
  - Read
7
8
  - Write
8
9
  - Edit
9
10
  - Glob
10
11
  - Grep
11
- - mcp__plugin_browser_browser__browser-render
12
- - mcp__plugin_browser_browser__browser-navigate
13
- - mcp__plugin_browser_browser__browser-evaluate
14
- - mcp__plugin_browser_browser__browser-resize
15
- - mcp__plugin_browser_browser__browser-screenshot
16
- - mcp__plugin_browser_browser__browser-wait-for
17
12
  ---
18
13
 
19
14
  # socials
20
15
 
21
- One skill, one subject at a time. You take a named **subject**, resolve it through a
22
- source adapter, and emit a publication bundle: exact-size PNG social tiles, captions
23
- for all five platforms, a gallery, and for any subject that can carry client data
24
- a redaction report. You **never post**. The bundle is for the operator to review
25
- and sign off. You ship no business content; every fact is the operator's own data,
26
- read from the resolved source.
27
-
28
- ## Inputs
29
-
30
- - **subject** a name (the business, the job, the topic).
31
- - **mode** one of `microsite`, `job`, `valuation`, `bespoke`.
32
- - **material** the path the adapter reads (a built `site/`, a job folder, or the
33
- operator's own photos and notes).
34
- - **output** — where the bundle lands (for `microsite`, the site's own `site/`).
35
-
36
- ## 1. Resolve the subject, and echo it
37
-
38
- Resolve the subject through its adapter in [`references/sources.md`](references/sources.md)
39
- to the normalized intermediate (`topic`, headline/sub candidates, copy facts,
40
- candidate images, `piiBearing`). **Before you render anything, state the resolved
41
- subject and the source mode back** — e.g. "Resolving job 1737 in `valuation` mode,
42
- this period's progress." A mis-resolution must be visible here, not discovered in a
43
- finished tile.
44
-
45
- ## 2. PII gate (if `piiBearing`)
46
-
47
- If the adapter's `piiBearing` is true (`job` and `valuation` always; `bespoke`
48
- unless the operator declared the material brand-safe), run the gate in
49
- [`references/pii-strip.md`](references/pii-strip.md) over **every** candidate image
50
- and **every** copy fact before any of it reaches a template. Write
51
- `redaction-report.md` reconciling every source photo and text field as
52
- *used / redacted / rejected*. If every photo is rejected, stop and ask for a usable
53
- image; never emit an imageless bundle. **Do not present a bundle without the report.**
54
- `microsite` skips this step (the site is already published copy).
55
-
56
- ## 3. Render the tiles (browser plugin, PNG)
57
-
58
- 1. Build the resolved render page: copy [`templates/og.html`](templates/og.html) to
59
- `<output>/og.html` and substitute `{{ CANDIDATES_JSON }}` with the vetted
60
- candidate set (`{ key: { img, pos, head, sub }, }`) plus the scalar tokens
61
- (palette, eyebrow, specs, domain, phone). The logo and hero images resolve from
62
- the output dir's `images/`.
63
- 2. Serve `<output>/` over a local HTTP server so relative paths and webfonts load
64
- (`cd <output> && python3 -m http.server <port>` via `Bash`; `file://` will not
65
- load the fonts).
66
- 3. For each tile, drive the on-device browser:
67
- - `browser-navigate` to `http://localhost:<port>/og.html?format=F&hero=H`,
68
- - `browser-resize` to the exact size — landscape `1200×628`, square `1200×1200`,
69
- portrait `1080×1350`,
70
- - `browser-evaluate` `document.fonts.ready.then(() => true)` (and a short settle)
71
- so the webfonts have landed before capture,
72
- - `browser-screenshot` to `<output>/socials/og-F-H.png`.
73
- A **person** hero (`isPerson: true`) renders **portrait only** — a landscape or
74
- square crop cuts off the head.
75
- 4. `Read` each finished PNG to confirm it rendered: logo legible, copy readable,
76
- scrim adequate. A **sub-20KB** file is a blank page (server not running, wrong
77
- port, or the page threw) — diagnose before re-running, never ship it.
78
-
79
- ## 4. Captions — all five platforms, plain register
80
-
81
- Build `<output>/social-posts.md` from [`templates/social-posts.md`](templates/social-posts.md):
82
- one block per tile, with copy for **Instagram, LinkedIn, TikTok, Facebook, and X**.
83
- Write every line in the plain register (the `plainly` skill): no em-dashes, no
84
- inflation vocabulary, no "it's not X, it's Y", no rhetorical openers, no emoji faking
85
- enthusiasm. For a `piiBearing` subject, no caption names the client, the address or
86
- postcode, any figure, any individual, or any email sender.
87
-
88
- ## 5. Gallery, and (microsite only) the og:image
89
-
90
- Build `<output>/socials.html` from [`templates/socials.html`](templates/socials.html):
91
- repeat the group block once per tile key. For a **person** hero (`isPerson: true`),
92
- write only the portrait `<div class="tile">` cell for that key — delete the landscape
93
- and square cells, since those files are never rendered for a person.
94
-
95
- The OG snippet (`<div class="snippet">…</div>`) is **microsite only**. In `microsite`
96
- mode, keep it, render the tiles into the site's `site/socials/`, and set the site's
97
- `og:image`/`twitter:image` to `<WEBSITE_URL>socials/og-landscape-main.png` (a true
98
- 1200×628 card beats a raw hero for link previews; `WEBSITE_URL` is the site's absolute
99
- URL from the brand pack). For a **job/valuation/bespoke** bundle there is no site to
100
- wire — **delete the whole `<div class="snippet">` block** from the copied template
101
- before substituting, so no `WEBSITE_URL` token survives.
102
-
103
- ## Output contract
104
-
105
- ```
106
- <output>/
107
- og.html # the resolved render page (kept for re-runs)
108
- socials/
109
- og-landscape-<key>.png og-square-<key>.png og-portrait-<key>.png # per tile
110
- og-portrait-<person-key>.png # a person hero: portrait only
111
- socials.html # gallery: each tile at size, download links
112
- social-posts.md # captions per tile, all five platforms
113
- redaction-report.md # piiBearing subjects only — the standing reconciliation
114
- ```
115
-
116
- ## Completion criteria
117
-
118
- - Every tile exists at its exact pixel size; a person tile is portrait only.
119
- - Captions cover all five platforms; `socials.html` previews each tile with a working
120
- download link.
121
- - For a `piiBearing` subject, `redaction-report.md` is present and accounts for
122
- **every** source photo and text field; no output names the client, the address, a
123
- figure, an individual, or an email sender.
124
- - In `microsite` mode, the site's `og:image`/`twitter:image` point at the landscape
125
- main tile.
126
- - `Read` each finished PNG — file size alone is not proof.
127
-
128
- ## When NOT to use
129
-
130
- - The operator wants to **post** — this skill never posts; it produces a reviewable
131
- bundle and stops.
132
- - The deliverable is the micro-site itself → `microsite`. The full marketing run
133
- (site + tiles) → `make-media`, which calls this skill in `microsite` mode.
16
+ SiteDesk renders tiles for four subjects: `microsite`, `bespoke`, `job`, and
17
+ `valuation`. The **render core is web-designer's** this skill owns no tile
18
+ template, gallery, caption, or PII-gate machinery. It owns one thing: turning a
19
+ SiteDesk **job** or **valuation** into a brand-safe brief that `web-designer:socials`
20
+ can render. You **never post**; the bundle is for the operator to review.
21
+
22
+ ## microsite and bespoke — delegate
23
+
24
+ Run `web-designer:socials` directly in the same mode on the same subject. For a
25
+ built `site/`, that is `microsite` mode (it also wires the site's `og:image`); for
26
+ an operator-chosen topic with the operator's own photos and notes, that is `bespoke`
27
+ mode (its PII gate runs unless the operator declares the material brand-safe). Add
28
+ nothing here web-designer owns the whole run.
29
+
30
+ ## job and valuation — resolve, then render as bespoke
31
+
32
+ These read the operator's live job state, so SiteDesk owns the read under a strict
33
+ money boundary; the rendering is still web-designer's.
34
+
35
+ 1. **Resolve the job under the money boundary.** Read the job folder per
36
+ [`references/sources.md`](references/sources.md) the `job` adapter for a
37
+ whole-job milestone, the `valuation` adapter for this cycle's progress. Read only
38
+ the allowed non-money progress fields; **never** the money fields or the issued
39
+ `valuations/` documents. `sources.md` is the exact contract; it is the one place
40
+ the money boundary is written.
41
+ 2. **Stage a brand-safe brief.** Write the resolved subject to a staging folder as a
42
+ bespoke brief: a `topic`, headline and sub candidates from the strongest progress
43
+ lines, `copyFacts` limited to neutral context (a stage name, a trade, a month, a
44
+ town — never a figure or a client identifier), and the `source/` photos as
45
+ candidate images. No money field and no client identifier reaches the brief.
46
+ 3. **Render through web-designer.** Run `web-designer:socials` in `bespoke` mode on
47
+ the staged brief. Its PII gate runs over the staged images and copy as a second
48
+ check and writes the redaction report; `web-designer:socials` never reads the job
49
+ folder or `state.json`. State the resolved subject back before rendering — e.g.
50
+ "Resolving job 1737 in valuation mode, this period's progress."
51
+
52
+ ## The money boundary is absolute
53
+
54
+ No tile, caption, or brief SiteDesk produces from a job may carry a contract,
55
+ valuation, or payment figure, any `pctComplete` value, or any client identifier.
56
+ The progress is the story; the figures never leave the job folder. This boundary is
57
+ enforced at the read in step 1 (only non-money fields are ever read) and re-checked
58
+ by web-designer's gate at step 3.
59
+
60
+ ## What this does not do
61
+
62
+ - It ships no render template, gallery, caption set, or PII-gate reference — those
63
+ live in `web-designer:socials`.
64
+ - It never posts, never deploys, and never reads a money field or a `valuations/`
65
+ document.
@@ -1,13 +1,17 @@
1
- # socials — source adapters
1
+ # socials — SiteDesk job source adapters
2
2
 
3
- The `socials` skill resolves a named **subject** to a normalized intermediate that
4
- the shared render core consumes. One adapter per subject kind. This file is the
5
- contract and the **extension point**: a future subject (a quote, a timesheet, a
6
- testimonial) is a new row here, not a new skill.
3
+ SiteDesk owns two subject adapters: `job` and `valuation`. Both read the operator's
4
+ live job state and resolve it to the normalized intermediate below, which
5
+ `web-designer:socials` renders in `bespoke` mode. The `microsite` and `bespoke`
6
+ adapters are web-designer's this file does not repeat them.
7
+
8
+ This file is the one place SiteDesk's **money boundary** is written. It is the
9
+ contract for what a job tile may and may not carry.
7
10
 
8
11
  ## The normalized intermediate
9
12
 
10
- Every adapter resolves its subject to this shape:
13
+ Each adapter resolves its subject to the shape `web-designer:socials` bespoke mode
14
+ consumes:
11
15
 
12
16
  ```
13
17
  {
@@ -16,40 +20,16 @@ Every adapter resolves its subject to this shape:
16
20
  subCandidates, // [string] — supporting lines, paired to headlines
17
21
  copyFacts, // { eyebrow, specs[], domain, phone } — overlay scalars
18
22
  candidateImages, // [{ key, path, isPerson }] — vetted backgrounds
19
- piiBearing // boolean — does this source carry client data?
23
+ piiBearing // boolean — job and valuation are always true
20
24
  }
21
25
  ```
22
26
 
23
- `candidateImages[].key` becomes the tile `hero` and the filename suffix
24
- (`og-<format>-<key>.png`). `isPerson: true` marks a hero that must render
25
- **portrait only** (a landscape or square crop cuts off the head). `piiBearing`
26
- decides whether the PII gate in [`pii-strip.md`](pii-strip.md) runs before any of
27
- this reaches a template.
28
-
29
- ## Adapters
30
-
31
- ### `microsite` — `piiBearing: false`
32
-
33
- Reads a built `site/` directory (the output of `microsite`): the brand pack
34
- (`brand.md` tokens, `images/logo.png`) and the curated hero photos at the fixed
35
- paths the micro-site uses:
36
-
37
- | key | image | leads with |
38
- |---|---|---|
39
- | `main` | `images/hero-01.webp` | the firm's lead line |
40
- | `interior` | `images/hero-03.webp` | a finishes line |
41
- | `build` | `images/hero-06.webp` | a process line |
42
- | `founder` | `images/founder-portrait.webp` | "meet the…" — **`isPerson: true`, portrait only** |
43
-
44
- `copyFacts` for `microsite` is the brand pack's own credential cells (sector,
45
- trades, areas, years) plus `domain`, `phone`, and the site's full `WEBSITE_URL` (the
46
- microsite-only og:image wiring needs the absolute URL, not just the domain).
27
+ `candidateImages[].key` becomes the tile `hero` and the filename suffix.
28
+ `isPerson: true` marks a hero that must render portrait only. `piiBearing: true`
29
+ means web-designer's PII gate runs over the staged brief before any of it reaches a
30
+ template.
47
31
 
48
- Brand-safe by construction (the site is already published copy), so the PII gate is
49
- skipped. This is the path `make-media` calls as its final stage; it also wires the
50
- site's `og:image`/`twitter:image` to the landscape `main` tile.
51
-
52
- ### `job` — `piiBearing: true`
32
+ ## `job` `piiBearing: true`
53
33
 
54
34
  A whole-job progress milestone. Topic = the site/job overall. Reads the job folder
55
35
  `{ACCOUNT_DIR}/jobs/{jobId}/`:
@@ -72,25 +52,10 @@ client should never see on a public tile. `lines[].id` and `variations[].number`
72
52
  **keys only** — use them to select a line, never as copy on a tile or caption (an
73
53
  operator may have keyed a line with a client name or address fragment).
74
54
 
75
- ### `valuation` — `piiBearing: true`
55
+ ## `valuation` — `piiBearing: true`
76
56
 
77
57
  The current valuation cycle's progress. Same job folder and the **same non-money
78
58
  fields** as `job`, but framed to **this period**: the lines and variations whose
79
59
  `pctComplete` advanced this cycle. Topic = "this period's progress on the job". The
80
60
  same money-field and `valuations/`-document exclusions apply — read the progress,
81
61
  never the figures.
82
-
83
- ### `bespoke` — `piiBearing: true` (operator may waive)
84
-
85
- An operator-chosen topic with the operator's own photos and notes. The operator
86
- supplies the topic, the headline/sub ideas, and the candidate images. The PII gate
87
- runs by default; the operator may declare the material brand-safe (their own
88
- marketing photos, no client identifiable) to skip it — that declaration is the only
89
- way the gate is waived for a bespoke subject.
90
-
91
- ## Adding a subject later
92
-
93
- A new subject kind (quote, timesheet, testimonial, …) is a new adapter section here:
94
- name what it reads, how it maps to the normalized intermediate, and its `piiBearing`
95
- value. No new skill, no new render core, no new caption register. If the source can
96
- carry client data, set `piiBearing: true` and the existing gate covers it.
@@ -1 +1 @@
1
- import{o as e}from"./chunk-CAM3fms7.js";import{A as t,L as n,M as r,N as i,O as a}from"./useSubAccountSwitcher-BVCMAnWK.js";import{i as o}from"./admin-types-hioowVct.js";import{b as s,x as c}from"./AdminShell-C5vWTy2n.js";import{t as l}from"./Checkbox-Chz6bfy-.js";var u=`admin-landing-redirected`,d=`/graph`;function f(e){return e.variant===`operator`?!1:e.appState===`chat`&&!e.alreadyRedirected}var p=e(n(),1);function m(e=`admin`){let[t,n]=(0,p.useState)(`loading`),[r,a]=(0,p.useState)(``),[s,l]=(0,p.useState)(``),[m,h]=(0,p.useState)(``),[g,_]=(0,p.useState)(!1),[v,y]=(0,p.useState)(!1),[b,x]=(0,p.useState)(!1),[S,C]=(0,p.useState)(!1),[w,T]=(0,p.useState)(!1),[E,D]=(0,p.useState)(null),[O,k]=(0,p.useState)(null),[A,j]=(0,p.useState)(void 0),[M,N]=(0,p.useState)(null),[P,F]=(0,p.useState)(void 0),[I,L]=(0,p.useState)(null),[R,z]=(0,p.useState)(null),[B,V]=(0,p.useState)(void 0),H=(0,p.useRef)(void 0),[U,W]=(0,p.useState)(!1),[G,K]=(0,p.useState)(!0);(0,p.useEffect)(()=>{typeof window>`u`||fetch(`/api/remote-auth/status`).then(e=>e.ok?e.json():null).then(e=>{e?.configured&&W(!0)}).catch(()=>{})},[]);let q=(0,p.useRef)(null),J=(0,p.useRef)(null);(0,p.useEffect)(()=>{async function e(){let e=null;try{e=sessionStorage.getItem(`maxy-admin-session-key`)}catch{}if(!e)return!1;try{let t=await fetch(`/api/admin/session?session_key=${encodeURIComponent(e)}`);if(t.status===401){try{sessionStorage.removeItem(`maxy-admin-session-key`)}catch{}return!1}if(!t.ok)return!1;let r=await t.json();D(r.session_key),z(r.sessionId??null),j(r.businessName),N(r.role??null),F(r.userName===void 0?null:r.userName),L(r.avatar??null);let i=o(r.thinkingView);return H.current=i,V(i),n(`chat`),!0}catch(e){return console.error(`[admin] session restore failed:`,e),!1}}async function t(r=2){try{let i=await fetch(`/api/health`);if(!i.ok){if(r>0)return await new Promise(e=>setTimeout(e,1500)),t(r-1);console.error(`[admin] health check returned ${i.status} after retries`),n(`set-pin`);return}let a=await i.json();if(!a.pin_configured){n(`set-pin`);return}K(!!a.claude_authenticated);let o=!1;try{o=new URLSearchParams(window.location.search).get(`connect`)===`claude`}catch{}if(o){try{let e=new URL(window.location.href);e.searchParams.delete(`connect`),window.history.replaceState(null,``,e.pathname+e.search+e.hash)}catch{}if(!a.claude_authenticated){n(`connect-claude`);return}}if(await e())return;n(`enter-pin`)}catch(e){if(r>0)return await new Promise(e=>setTimeout(e,1500)),t(r-1);console.error(`[admin] health check failed:`,e),n(`set-pin`)}}t()},[]),(0,p.useEffect)(()=>{t===`chat`&&fetch(`/api/admin/claude-info`).then(e=>{if(e.ok)return e.json()}).then(e=>{e&&k(e)}).catch(()=>{})},[t]),(0,p.useEffect)(()=>{if(typeof window>`u`)return;let n=!1;try{n=sessionStorage.getItem(u)===`1`}catch{}if(f({appState:t,alreadyRedirected:n,variant:e})){try{sessionStorage.setItem(u,`1`)}catch{}console.info(`[admin-ui] landing-redirect target=${d}`),window.location.replace(d)}},[t,e]);let Y=(0,p.useRef)(null);(0,p.useEffect)(()=>{if(t!==`chat`)return;let e=setInterval(async()=>{try{let e=await fetch(`/api/health`);if(e.ok){let t=await e.json();K(t.auth_status!==`dead`&&t.auth_status!==`missing`)}}catch{}if(E)try{let e=await fetch(`/api/admin/session?session_key=${encodeURIComponent(E)}`);if(e.status!==401)return;let t=(await e.clone().json().catch(()=>null))?.code??`unknown-401`;if(t===`remote-auth-required`){i(`heartbeat`,`/api/admin/session`);return}console.warn(`[admin-auth] outcome=heartbeat-detected-expiry code=${t}`),Y.current?.()}catch{}},300*1e3);return()=>clearInterval(e)},[t,E]),(0,p.useEffect)(()=>{t===`connect-claude`&&fetch(`/api/health`).then(e=>e.ok?e.json():null).then(e=>{e?.claude_authenticated&&(K(!0),n(`enter-pin`))}).catch(()=>{})},[t]);async function X(e,t){y(!0);try{let r=await fetch(`/api/admin/session`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:e,...t?{accountId:t}:{}})});if(!r.ok){let e=await r.json().catch(()=>({}));if(r.status===401&&e?.code===`remote-auth-required`){i(`enter-pin`,`/api/admin/session`);return}h(e.error||`Invalid PIN`);return}let s=await r.json();D(s.session_key),z(s.sessionId??null),j(s.businessName),N(s.role??null),F(s.userName===void 0?null:s.userName),L(s.avatar??null);let c=o(s.thinkingView);H.current=c,V(c);try{sessionStorage.setItem(`maxy-admin-session-key`,s.session_key)}catch{}a(``),n(`chat`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1)}}let Z=(0,p.useCallback)(async e=>{if(e.preventDefault(),v)return;h(``);let t=s.trim();if(!t){h(`Please enter your name.`);return}if(r.length<4){h(`PIN must be at least 4 characters.`);return}let o=r;y(!0);try{let e=await fetch(`/api/onboarding/set-pin`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:o,name:t})});if(!e.ok){let t=await e.json().catch(()=>({}));if(e.status===401&&t?.code===`remote-auth-required`){i(`set-pin`,`/api/onboarding/set-pin`);return}if(e.status===409){console.log(`[admin] PIN already configured — re-checking health`);try{let e=await fetch(`/api/health`);if(e.ok){let r=await e.json();r.pin_configured&&r.claude_authenticated?n(`enter-pin`):r.pin_configured?n(`connect-claude`):h(t.error||`Failed to set PIN.`)}else n(`enter-pin`)}catch{n(`enter-pin`)}return}h(t.error||`Failed to set PIN.`);return}let r=await fetch(`/api/health`);if((r.ok?await r.json():null)?.claude_authenticated){await X(o);return}a(``),n(`connect-claude`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1)}},[r,v,s]),Q=(0,p.useCallback)(async e=>{e.preventDefault(),h(``),await X(r)},[r]),ee=(0,p.useCallback)(async()=>{T(!0);try{if(!await c())return console.warn(`[admin-ui] claude-disconnect not verified — credentials may persist; staying put`),!1;K(!1),D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(u)}catch{}return n(`connect-claude`),!0}finally{T(!1)}},[]),$=(0,p.useCallback)(()=>{D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(u)}catch{}a(``),h(``),n(`enter-pin`)},[]);return(0,p.useEffect)(()=>{Y.current=$},[$]),{appState:t,setAppState:n,pin:r,setPin:a,operatorName:s,setOperatorName:l,pinError:m,setPinError:h,showPin:g,setShowPin:_,pinLoading:v,authPolling:b,setAuthPolling:x,authLoading:S,setAuthLoading:C,disconnecting:w,cacheKey:E,setCacheKey:D,claudeInfo:O,setClaudeInfo:k,businessName:A,role:M,userName:P,userAvatar:I,sessionId:R,setSessionId:z,expandAll:B,setExpandAll:V,expandAllDefaultRef:H,remoteAuthEnabled:U,claudeConnected:G,pinInputRef:q,setPinFormRef:J,handleSetPin:Z,handleLogin:Q,handleDisconnect:ee,handleLogout:$}}var h=r();function g({inputRef:e,value:t,onChange:n,onComplete:r,showPin:i,autoFocus:a}){let o=(0,p.useRef)([]);function s(e,r){r.key===`Backspace`?(r.preventDefault(),t[e]?n(t.slice(0,e)+t.slice(e+1)):e>0&&(n(t.slice(0,e-1)+t.slice(e)),o.current[e-1]?.focus())):r.key===`ArrowLeft`&&e>0?o.current[e-1]?.focus():r.key===`ArrowRight`&&e<5?o.current[e+1]?.focus():r.key===`Enter`&&(r.preventDefault(),r.currentTarget.form?.requestSubmit())}function c(e,i){let a=i.nativeEvent.data;if(!a||!/^\d$/.test(a))return;let s=t.split(``);for(s[e]=a;s.length<e;)s.push(``);let c=s.join(``).replace(/\D/g,``).slice(0,6);n(c),c.length===6?r?.(c):e<5&&o.current[e+1]?.focus()}function l(e){e.preventDefault();let t=e.clipboardData.getData(`text`).replace(/\D/g,``).slice(0,6);t&&(n(t),t.length===6?r?.(t):o.current[t.length]?.focus())}return(0,h.jsx)(`div`,{className:`pin-field`,children:Array.from({length:6}).map((n,r)=>(0,h.jsx)(`input`,{ref:t=>{o.current[r]=t,r===0&&e&&(e.current=t)},type:`text`,inputMode:`numeric`,className:`pin-box${t[r]?` pin-box-filled`:``}`,value:t[r]?i?t[r]:`•`:``,onKeyDown:e=>s(r,e),onInput:e=>c(r,e),onPaste:l,onFocus:e=>e.target.select(),autoFocus:a&&r===0,autoComplete:`off`,maxLength:1,"aria-label":`PIN digit ${r+1}`},r))})}function _(e){let{pin:n,setPin:r,showPin:i,setShowPin:o,pinLoading:c,pinError:u,pinInputRef:d,setPinFormRef:f,onSubmit:p,operatorName:m,setOperatorName:_}=e;return(0,h.jsx)(`div`,{className:`connect-page`,children:(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsx)(`img`,{src:t,alt:a.productName,className:`connect-logo connect-logo--maxy`}),!a.logoContainsName&&(0,h.jsxs)(`h1`,{className:`connect-title`,children:[`Welcome to `,a.productName]}),(0,h.jsxs)(`p`,{className:`connect-subtitle`,children:[`Tell `,a.productName,` who you are, then choose a PIN.`]}),(0,h.jsxs)(`form`,{ref:f,onSubmit:p,className:`connect-pin-form`,children:[(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(`input`,{type:`text`,className:`connect-name-input`,placeholder:`Your full name`,value:m,onChange:e=>_(e.target.value),autoComplete:`name`,autoFocus:!0,required:!0,"aria-label":`Your full name`}),(0,h.jsx)(`div`,{style:{width:38,flexShrink:0},"aria-hidden":`true`})]}),(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(g,{inputRef:d,value:n,onChange:r,onComplete:()=>{},showPin:i}),(0,h.jsx)(s,{variant:`send`,type:`submit`,disabled:!n||!m.trim(),loading:c,"aria-label":`Set PIN`,children:(0,h.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,h.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,h.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,h.jsx)(l,{checked:i,onChange:()=>o(e=>!e),label:`Show PIN`})]}),u&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:u})]})})}function v(e){let{pin:n,setPin:r,showPin:i,setShowPin:o,pinLoading:c,pinError:u,pinInputRef:d,onSubmit:f,remoteAuthEnabled:p,onSignOutRemote:m}=e;return(0,h.jsxs)(`div`,{className:`connect-page`,children:[p&&m&&(0,h.jsx)(`button`,{type:`button`,className:`connect-signout`,onClick:m,children:`Sign out`}),(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsx)(`img`,{src:t,alt:a.productName,className:`connect-logo connect-logo--maxy`}),!a.logoContainsName&&(0,h.jsx)(`h1`,{className:`connect-title`,children:a.productName}),(0,h.jsxs)(`form`,{onSubmit:f,className:`connect-pin-form`,children:[(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(g,{inputRef:d,value:n,onChange:r,onComplete:()=>{},showPin:i,autoFocus:!0}),(0,h.jsx)(s,{variant:`send`,type:`submit`,disabled:!n,loading:c,children:(0,h.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,h.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,h.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,h.jsx)(`div`,{className:`pin-options`,children:(0,h.jsx)(l,{checked:i,onChange:()=>o(e=>!e),label:`Show PIN`})})]}),u&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:u})]})]})}function y(e){let{authPolling:n,setAuthPolling:r,authLoading:i,setAuthLoading:o,pinError:c,setPinError:l,setAppState:u}=e,[d,f]=(0,p.useState)(!1),[m,g]=(0,p.useState)(!1),[_,v]=(0,p.useState)(``),[y,b]=(0,p.useState)(``),[x,S]=(0,p.useState)(``);async function C(){S(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`paste-code`,code:y})})).json();e.accepted?(S(`Code submitted. Finishing sign-in…`),b(``)):S(e.error||`Could not submit code.`)}catch(e){console.error(`[admin] paste-code error:`,e),S(`Could not submit code.`)}}async function w(){g(!0),l(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`launch-browser`})})).json();e.launched?f(!0):e.error&&l(e.error)}catch(e){console.error(`[admin] browser launch error:`,e),l(`Could not launch browser.`)}g(!1)}async function T(){o(!0),l(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`})).json();if(e.started){r(!0),f(!0),o(!1),v(e.transport||``);for(let e=0;e<120;e++)if(await new Promise(e=>setTimeout(e,2e3)),(await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`wait`})})).json()).authenticated){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),u(`enter-pin`);return}l(`Timed out waiting for sign-in. Try again.`),r(!1)}else e.error&&l(e.error)}catch(e){console.error(`[admin] auth flow error:`,e),l(`Could not start auth flow.`)}o(!1)}async function E(){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),r(!1),l(``)}return n||d?(0,h.jsxs)(`div`,{style:{display:`flex`,flexDirection:`column`,height:`100dvh`,overflow:`auto`},children:[(0,h.jsxs)(`header`,{className:`chat-header`,style:{paddingBottom:`12px`,flexShrink:0,position:`relative`,maxWidth:`680px`,width:`100%`,margin:`0 auto`,padding:`24px 20px 12px`},children:[n?(0,h.jsx)(`button`,{onClick:E,style:{position:`absolute`,top:`12px`,right:`12px`,background:`none`,border:`none`,color:`#999`,fontSize:`13px`,cursor:`pointer`,padding:`4px 8px`},"aria-label":`Cancel`,children:`✕`}):(0,h.jsx)(`button`,{onClick:()=>f(!1),style:{position:`absolute`,top:`12px`,right:`12px`,background:`none`,border:`none`,color:`#999`,fontSize:`13px`,cursor:`pointer`,padding:`4px 8px`},"aria-label":`Close browser`,children:`✕`}),(0,h.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`chat-logo`}),(0,h.jsx)(`h1`,{className:`chat-tagline`,children:`Connect Claude`}),(0,h.jsx)(`p`,{className:`chat-intro`,children:n?`Sign in and authorize in the browser below.`:`Open your email or prepare your accounts, then sign in.`}),!n&&(0,h.jsx)(`div`,{style:{marginTop:`12px`},children:(0,h.jsx)(s,{variant:`primary`,onClick:T,disabled:i,children:i?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`})}),n&&_===`native`&&(0,h.jsxs)(`div`,{style:{marginTop:`14px`,maxWidth:`420px`,margin:`14px auto 0`,display:`flex`,flexDirection:`column`,gap:`8px`},children:[(0,h.jsx)(`label`,{style:{fontSize:`12px`,color:`#999`,lineHeight:1.4},children:`If the browser didn't finish sign-in, paste the code or full redirect URL from Claude's page:`}),(0,h.jsxs)(`div`,{style:{display:`flex`,gap:`8px`},children:[(0,h.jsx)(`input`,{type:`text`,value:y,onChange:e=>b(e.target.value),placeholder:`Paste code or redirect URL`,style:{flex:1,padding:`8px 10px`,fontSize:`13px`,border:`1px solid #ccc`,borderRadius:`6px`}}),(0,h.jsx)(s,{variant:`primary`,onClick:C,disabled:!y.trim(),children:`Submit`})]}),x&&(0,h.jsx)(`p`,{style:{fontSize:`12px`,color:`#999`,margin:0},children:x})]})]}),(0,h.jsx)(`div`,{style:{flex:1,display:`flex`,flexDirection:`column`,minHeight:0,gap:`10px`,padding:`0 0 16px`},children:(0,h.jsx)(`iframe`,{src:`/vnc-viewer.html`,style:{flex:1,width:`100%`,minHeight:0,border:`none`,background:`#111`,display:`block`},title:`Claude Sign-in`})}),c&&(0,h.jsx)(`p`,{className:`admin-pin-error`,style:{textAlign:`center`,padding:`0 20px 16px`},children:c})]}):(0,h.jsx)(`div`,{className:`connect-page`,children:(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsxs)(`div`,{className:`connect-logos`,children:[(0,h.jsx)(`div`,{className:`connect-logo-wrap`,children:(0,h.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`connect-logo`})}),(0,h.jsx)(`svg`,{className:`connect-arrow`,viewBox:`0 0 48 24`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:(0,h.jsx)(`path`,{d:`M0 12h44m0 0l-8-8m8 8l-8 8`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`})}),(0,h.jsxs)(`div`,{className:`connect-logo-wrap`,children:[(0,h.jsx)(`img`,{src:t,alt:a.productName,className:`connect-logo connect-logo--maxy`}),!a.logoContainsName&&(0,h.jsx)(`span`,{className:`connect-logo-label`,children:a.productName})]})]}),(0,h.jsxs)(`h1`,{className:`connect-title`,children:[`Connect Claude to power `,a.productName]}),(0,h.jsx)(`p`,{className:`connect-subtitle`,children:`Sign in with your Anthropic account to get started.`}),(0,h.jsx)(s,{variant:`primary`,onClick:T,disabled:i,children:i?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`}),(0,h.jsx)(`p`,{style:{marginTop:`6px`,fontSize:`11px`,color:`#999`,maxWidth:`300px`,textAlign:`center`,lineHeight:`1.4`},children:`First time? You may need to sign into your email and Anthropic account in the browser before connecting.`}),(0,h.jsx)(`button`,{onClick:w,disabled:m,style:{marginTop:`12px`,background:`none`,border:`none`,color:`var(--color-primary, #666)`,fontSize:`13px`,cursor:`pointer`,textDecoration:`underline`,textUnderlineOffset:`3px`},children:m?`Launching…`:`Open browser first`}),c&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:c})]})})}function b({auth:e}){return e.appState===`loading`?(0,h.jsx)(`div`,{className:`connect-page`}):e.appState===`set-pin`?(0,h.jsx)(_,{pin:e.pin,setPin:e.setPin,showPin:e.showPin,setShowPin:e.setShowPin,pinLoading:e.pinLoading,pinError:e.pinError,pinInputRef:e.pinInputRef,setPinFormRef:e.setPinFormRef,onSubmit:e.handleSetPin,operatorName:e.operatorName,setOperatorName:e.setOperatorName}):e.appState===`connect-claude`?(0,h.jsx)(y,{authPolling:e.authPolling,setAuthPolling:e.setAuthPolling,authLoading:e.authLoading,setAuthLoading:e.setAuthLoading,pinError:e.pinError,setPinError:e.setPinError,setAppState:e.setAppState}):e.appState===`enter-pin`?(0,h.jsx)(v,{pin:e.pin,setPin:e.setPin,showPin:e.showPin,setShowPin:e.setShowPin,pinLoading:e.pinLoading,pinError:e.pinError,pinInputRef:e.pinInputRef,onSubmit:e.handleLogin,remoteAuthEnabled:e.remoteAuthEnabled,onSignOutRemote:()=>{console.info(`[admin-ui] remote-auth sign-out → /__remote-auth/logout`),window.location.href=`/__remote-auth/logout`}}):null}export{m as n,b as t};
1
+ import{o as e}from"./chunk-CAM3fms7.js";import{A as t,L as n,M as r,N as i,O as a}from"./useSubAccountSwitcher-Bg0RH3Gk.js";import{i as o}from"./admin-types-hioowVct.js";import{b as s,x as c}from"./AdminShell-BihejKg_.js";import{t as l}from"./Checkbox-Ds004CBE.js";var u=`admin-landing-redirected`,d=`/graph`;function f(e){return e.variant===`operator`?!1:e.appState===`chat`&&!e.alreadyRedirected}var p=e(n(),1);function m(e=`admin`){let[t,n]=(0,p.useState)(`loading`),[r,a]=(0,p.useState)(``),[s,l]=(0,p.useState)(``),[m,h]=(0,p.useState)(``),[g,_]=(0,p.useState)(!1),[v,y]=(0,p.useState)(!1),[b,x]=(0,p.useState)(!1),[S,C]=(0,p.useState)(!1),[w,T]=(0,p.useState)(!1),[E,D]=(0,p.useState)(null),[O,k]=(0,p.useState)(null),[A,j]=(0,p.useState)(void 0),[M,N]=(0,p.useState)(null),[P,F]=(0,p.useState)(void 0),[I,L]=(0,p.useState)(null),[R,z]=(0,p.useState)(null),[B,V]=(0,p.useState)(void 0),H=(0,p.useRef)(void 0),[U,W]=(0,p.useState)(!1),[G,K]=(0,p.useState)(!0);(0,p.useEffect)(()=>{typeof window>`u`||fetch(`/api/remote-auth/status`).then(e=>e.ok?e.json():null).then(e=>{e?.configured&&W(!0)}).catch(()=>{})},[]);let q=(0,p.useRef)(null),J=(0,p.useRef)(null);(0,p.useEffect)(()=>{async function e(){let e=null;try{e=sessionStorage.getItem(`maxy-admin-session-key`)}catch{}if(!e)return!1;try{let t=await fetch(`/api/admin/session?session_key=${encodeURIComponent(e)}`);if(t.status===401){try{sessionStorage.removeItem(`maxy-admin-session-key`)}catch{}return!1}if(!t.ok)return!1;let r=await t.json();D(r.session_key),z(r.sessionId??null),j(r.businessName),N(r.role??null),F(r.userName===void 0?null:r.userName),L(r.avatar??null);let i=o(r.thinkingView);return H.current=i,V(i),n(`chat`),!0}catch(e){return console.error(`[admin] session restore failed:`,e),!1}}async function t(r=2){try{let i=await fetch(`/api/health`);if(!i.ok){if(r>0)return await new Promise(e=>setTimeout(e,1500)),t(r-1);console.error(`[admin] health check returned ${i.status} after retries`),n(`set-pin`);return}let a=await i.json();if(!a.pin_configured){n(`set-pin`);return}K(!!a.claude_authenticated);let o=!1;try{o=new URLSearchParams(window.location.search).get(`connect`)===`claude`}catch{}if(o){try{let e=new URL(window.location.href);e.searchParams.delete(`connect`),window.history.replaceState(null,``,e.pathname+e.search+e.hash)}catch{}if(!a.claude_authenticated){n(`connect-claude`);return}}if(await e())return;n(`enter-pin`)}catch(e){if(r>0)return await new Promise(e=>setTimeout(e,1500)),t(r-1);console.error(`[admin] health check failed:`,e),n(`set-pin`)}}t()},[]),(0,p.useEffect)(()=>{t===`chat`&&fetch(`/api/admin/claude-info`).then(e=>{if(e.ok)return e.json()}).then(e=>{e&&k(e)}).catch(()=>{})},[t]),(0,p.useEffect)(()=>{if(typeof window>`u`)return;let n=!1;try{n=sessionStorage.getItem(u)===`1`}catch{}if(f({appState:t,alreadyRedirected:n,variant:e})){try{sessionStorage.setItem(u,`1`)}catch{}console.info(`[admin-ui] landing-redirect target=${d}`),window.location.replace(d)}},[t,e]);let Y=(0,p.useRef)(null);(0,p.useEffect)(()=>{if(t!==`chat`)return;let e=setInterval(async()=>{try{let e=await fetch(`/api/health`);if(e.ok){let t=await e.json();K(t.auth_status!==`dead`&&t.auth_status!==`missing`)}}catch{}if(E)try{let e=await fetch(`/api/admin/session?session_key=${encodeURIComponent(E)}`);if(e.status!==401)return;let t=(await e.clone().json().catch(()=>null))?.code??`unknown-401`;if(t===`remote-auth-required`){i(`heartbeat`,`/api/admin/session`);return}console.warn(`[admin-auth] outcome=heartbeat-detected-expiry code=${t}`),Y.current?.()}catch{}},300*1e3);return()=>clearInterval(e)},[t,E]),(0,p.useEffect)(()=>{t===`connect-claude`&&fetch(`/api/health`).then(e=>e.ok?e.json():null).then(e=>{e?.claude_authenticated&&(K(!0),n(`enter-pin`))}).catch(()=>{})},[t]);async function X(e,t){y(!0);try{let r=await fetch(`/api/admin/session`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:e,...t?{accountId:t}:{}})});if(!r.ok){let e=await r.json().catch(()=>({}));if(r.status===401&&e?.code===`remote-auth-required`){i(`enter-pin`,`/api/admin/session`);return}h(e.error||`Invalid PIN`);return}let s=await r.json();D(s.session_key),z(s.sessionId??null),j(s.businessName),N(s.role??null),F(s.userName===void 0?null:s.userName),L(s.avatar??null);let c=o(s.thinkingView);H.current=c,V(c);try{sessionStorage.setItem(`maxy-admin-session-key`,s.session_key)}catch{}a(``),n(`chat`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1)}}let Z=(0,p.useCallback)(async e=>{if(e.preventDefault(),v)return;h(``);let t=s.trim();if(!t){h(`Please enter your name.`);return}if(r.length<4){h(`PIN must be at least 4 characters.`);return}let o=r;y(!0);try{let e=await fetch(`/api/onboarding/set-pin`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:o,name:t})});if(!e.ok){let t=await e.json().catch(()=>({}));if(e.status===401&&t?.code===`remote-auth-required`){i(`set-pin`,`/api/onboarding/set-pin`);return}if(e.status===409){console.log(`[admin] PIN already configured — re-checking health`);try{let e=await fetch(`/api/health`);if(e.ok){let r=await e.json();r.pin_configured&&r.claude_authenticated?n(`enter-pin`):r.pin_configured?n(`connect-claude`):h(t.error||`Failed to set PIN.`)}else n(`enter-pin`)}catch{n(`enter-pin`)}return}h(t.error||`Failed to set PIN.`);return}let r=await fetch(`/api/health`);if((r.ok?await r.json():null)?.claude_authenticated){await X(o);return}a(``),n(`connect-claude`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1)}},[r,v,s]),Q=(0,p.useCallback)(async e=>{e.preventDefault(),h(``),await X(r)},[r]),ee=(0,p.useCallback)(async()=>{T(!0);try{if(!await c())return console.warn(`[admin-ui] claude-disconnect not verified — credentials may persist; staying put`),!1;K(!1),D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(u)}catch{}return n(`connect-claude`),!0}finally{T(!1)}},[]),$=(0,p.useCallback)(()=>{D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(u)}catch{}a(``),h(``),n(`enter-pin`)},[]);return(0,p.useEffect)(()=>{Y.current=$},[$]),{appState:t,setAppState:n,pin:r,setPin:a,operatorName:s,setOperatorName:l,pinError:m,setPinError:h,showPin:g,setShowPin:_,pinLoading:v,authPolling:b,setAuthPolling:x,authLoading:S,setAuthLoading:C,disconnecting:w,cacheKey:E,setCacheKey:D,claudeInfo:O,setClaudeInfo:k,businessName:A,role:M,userName:P,userAvatar:I,sessionId:R,setSessionId:z,expandAll:B,setExpandAll:V,expandAllDefaultRef:H,remoteAuthEnabled:U,claudeConnected:G,pinInputRef:q,setPinFormRef:J,handleSetPin:Z,handleLogin:Q,handleDisconnect:ee,handleLogout:$}}var h=r();function g({inputRef:e,value:t,onChange:n,onComplete:r,showPin:i,autoFocus:a}){let o=(0,p.useRef)([]);function s(e,r){r.key===`Backspace`?(r.preventDefault(),t[e]?n(t.slice(0,e)+t.slice(e+1)):e>0&&(n(t.slice(0,e-1)+t.slice(e)),o.current[e-1]?.focus())):r.key===`ArrowLeft`&&e>0?o.current[e-1]?.focus():r.key===`ArrowRight`&&e<5?o.current[e+1]?.focus():r.key===`Enter`&&(r.preventDefault(),r.currentTarget.form?.requestSubmit())}function c(e,i){let a=i.nativeEvent.data;if(!a||!/^\d$/.test(a))return;let s=t.split(``);for(s[e]=a;s.length<e;)s.push(``);let c=s.join(``).replace(/\D/g,``).slice(0,6);n(c),c.length===6?r?.(c):e<5&&o.current[e+1]?.focus()}function l(e){e.preventDefault();let t=e.clipboardData.getData(`text`).replace(/\D/g,``).slice(0,6);t&&(n(t),t.length===6?r?.(t):o.current[t.length]?.focus())}return(0,h.jsx)(`div`,{className:`pin-field`,children:Array.from({length:6}).map((n,r)=>(0,h.jsx)(`input`,{ref:t=>{o.current[r]=t,r===0&&e&&(e.current=t)},type:`text`,inputMode:`numeric`,className:`pin-box${t[r]?` pin-box-filled`:``}`,value:t[r]?i?t[r]:`•`:``,onKeyDown:e=>s(r,e),onInput:e=>c(r,e),onPaste:l,onFocus:e=>e.target.select(),autoFocus:a&&r===0,autoComplete:`off`,maxLength:1,"aria-label":`PIN digit ${r+1}`},r))})}function _(e){let{pin:n,setPin:r,showPin:i,setShowPin:o,pinLoading:c,pinError:u,pinInputRef:d,setPinFormRef:f,onSubmit:p,operatorName:m,setOperatorName:_}=e;return(0,h.jsx)(`div`,{className:`connect-page`,children:(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsx)(`img`,{src:t,alt:a.productName,className:`connect-logo connect-logo--maxy`}),!a.logoContainsName&&(0,h.jsxs)(`h1`,{className:`connect-title`,children:[`Welcome to `,a.productName]}),(0,h.jsxs)(`p`,{className:`connect-subtitle`,children:[`Tell `,a.productName,` who you are, then choose a PIN.`]}),(0,h.jsxs)(`form`,{ref:f,onSubmit:p,className:`connect-pin-form`,children:[(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(`input`,{type:`text`,className:`connect-name-input`,placeholder:`Your full name`,value:m,onChange:e=>_(e.target.value),autoComplete:`name`,autoFocus:!0,required:!0,"aria-label":`Your full name`}),(0,h.jsx)(`div`,{style:{width:38,flexShrink:0},"aria-hidden":`true`})]}),(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(g,{inputRef:d,value:n,onChange:r,onComplete:()=>{},showPin:i}),(0,h.jsx)(s,{variant:`send`,type:`submit`,disabled:!n||!m.trim(),loading:c,"aria-label":`Set PIN`,children:(0,h.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,h.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,h.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,h.jsx)(l,{checked:i,onChange:()=>o(e=>!e),label:`Show PIN`})]}),u&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:u})]})})}function v(e){let{pin:n,setPin:r,showPin:i,setShowPin:o,pinLoading:c,pinError:u,pinInputRef:d,onSubmit:f,remoteAuthEnabled:p,onSignOutRemote:m}=e;return(0,h.jsxs)(`div`,{className:`connect-page`,children:[p&&m&&(0,h.jsx)(`button`,{type:`button`,className:`connect-signout`,onClick:m,children:`Sign out`}),(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsx)(`img`,{src:t,alt:a.productName,className:`connect-logo connect-logo--maxy`}),!a.logoContainsName&&(0,h.jsx)(`h1`,{className:`connect-title`,children:a.productName}),(0,h.jsxs)(`form`,{onSubmit:f,className:`connect-pin-form`,children:[(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(g,{inputRef:d,value:n,onChange:r,onComplete:()=>{},showPin:i,autoFocus:!0}),(0,h.jsx)(s,{variant:`send`,type:`submit`,disabled:!n,loading:c,children:(0,h.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,h.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,h.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,h.jsx)(`div`,{className:`pin-options`,children:(0,h.jsx)(l,{checked:i,onChange:()=>o(e=>!e),label:`Show PIN`})})]}),u&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:u})]})]})}function y(e){let{authPolling:n,setAuthPolling:r,authLoading:i,setAuthLoading:o,pinError:c,setPinError:l,setAppState:u}=e,[d,f]=(0,p.useState)(!1),[m,g]=(0,p.useState)(!1),[_,v]=(0,p.useState)(``),[y,b]=(0,p.useState)(``),[x,S]=(0,p.useState)(``);async function C(){S(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`paste-code`,code:y})})).json();e.accepted?(S(`Code submitted. Finishing sign-in…`),b(``)):S(e.error||`Could not submit code.`)}catch(e){console.error(`[admin] paste-code error:`,e),S(`Could not submit code.`)}}async function w(){g(!0),l(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`launch-browser`})})).json();e.launched?f(!0):e.error&&l(e.error)}catch(e){console.error(`[admin] browser launch error:`,e),l(`Could not launch browser.`)}g(!1)}async function T(){o(!0),l(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`})).json();if(e.started){r(!0),f(!0),o(!1),v(e.transport||``);for(let e=0;e<120;e++)if(await new Promise(e=>setTimeout(e,2e3)),(await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`wait`})})).json()).authenticated){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),u(`enter-pin`);return}l(`Timed out waiting for sign-in. Try again.`),r(!1)}else e.error&&l(e.error)}catch(e){console.error(`[admin] auth flow error:`,e),l(`Could not start auth flow.`)}o(!1)}async function E(){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),r(!1),l(``)}return n||d?(0,h.jsxs)(`div`,{style:{display:`flex`,flexDirection:`column`,height:`100dvh`,overflow:`auto`},children:[(0,h.jsxs)(`header`,{className:`chat-header`,style:{paddingBottom:`12px`,flexShrink:0,position:`relative`,maxWidth:`680px`,width:`100%`,margin:`0 auto`,padding:`24px 20px 12px`},children:[n?(0,h.jsx)(`button`,{onClick:E,style:{position:`absolute`,top:`12px`,right:`12px`,background:`none`,border:`none`,color:`#999`,fontSize:`13px`,cursor:`pointer`,padding:`4px 8px`},"aria-label":`Cancel`,children:`✕`}):(0,h.jsx)(`button`,{onClick:()=>f(!1),style:{position:`absolute`,top:`12px`,right:`12px`,background:`none`,border:`none`,color:`#999`,fontSize:`13px`,cursor:`pointer`,padding:`4px 8px`},"aria-label":`Close browser`,children:`✕`}),(0,h.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`chat-logo`}),(0,h.jsx)(`h1`,{className:`chat-tagline`,children:`Connect Claude`}),(0,h.jsx)(`p`,{className:`chat-intro`,children:n?`Sign in and authorize in the browser below.`:`Open your email or prepare your accounts, then sign in.`}),!n&&(0,h.jsx)(`div`,{style:{marginTop:`12px`},children:(0,h.jsx)(s,{variant:`primary`,onClick:T,disabled:i,children:i?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`})}),n&&_===`native`&&(0,h.jsxs)(`div`,{style:{marginTop:`14px`,maxWidth:`420px`,margin:`14px auto 0`,display:`flex`,flexDirection:`column`,gap:`8px`},children:[(0,h.jsx)(`label`,{style:{fontSize:`12px`,color:`#999`,lineHeight:1.4},children:`If the browser didn't finish sign-in, paste the code or full redirect URL from Claude's page:`}),(0,h.jsxs)(`div`,{style:{display:`flex`,gap:`8px`},children:[(0,h.jsx)(`input`,{type:`text`,value:y,onChange:e=>b(e.target.value),placeholder:`Paste code or redirect URL`,style:{flex:1,padding:`8px 10px`,fontSize:`13px`,border:`1px solid #ccc`,borderRadius:`6px`}}),(0,h.jsx)(s,{variant:`primary`,onClick:C,disabled:!y.trim(),children:`Submit`})]}),x&&(0,h.jsx)(`p`,{style:{fontSize:`12px`,color:`#999`,margin:0},children:x})]})]}),(0,h.jsx)(`div`,{style:{flex:1,display:`flex`,flexDirection:`column`,minHeight:0,gap:`10px`,padding:`0 0 16px`},children:(0,h.jsx)(`iframe`,{src:`/vnc-viewer.html`,style:{flex:1,width:`100%`,minHeight:0,border:`none`,background:`#111`,display:`block`},title:`Claude Sign-in`})}),c&&(0,h.jsx)(`p`,{className:`admin-pin-error`,style:{textAlign:`center`,padding:`0 20px 16px`},children:c})]}):(0,h.jsx)(`div`,{className:`connect-page`,children:(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsxs)(`div`,{className:`connect-logos`,children:[(0,h.jsx)(`div`,{className:`connect-logo-wrap`,children:(0,h.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`connect-logo`})}),(0,h.jsx)(`svg`,{className:`connect-arrow`,viewBox:`0 0 48 24`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:(0,h.jsx)(`path`,{d:`M0 12h44m0 0l-8-8m8 8l-8 8`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`})}),(0,h.jsxs)(`div`,{className:`connect-logo-wrap`,children:[(0,h.jsx)(`img`,{src:t,alt:a.productName,className:`connect-logo connect-logo--maxy`}),!a.logoContainsName&&(0,h.jsx)(`span`,{className:`connect-logo-label`,children:a.productName})]})]}),(0,h.jsxs)(`h1`,{className:`connect-title`,children:[`Connect Claude to power `,a.productName]}),(0,h.jsx)(`p`,{className:`connect-subtitle`,children:`Sign in with your Anthropic account to get started.`}),(0,h.jsx)(s,{variant:`primary`,onClick:T,disabled:i,children:i?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`}),(0,h.jsx)(`p`,{style:{marginTop:`6px`,fontSize:`11px`,color:`#999`,maxWidth:`300px`,textAlign:`center`,lineHeight:`1.4`},children:`First time? You may need to sign into your email and Anthropic account in the browser before connecting.`}),(0,h.jsx)(`button`,{onClick:w,disabled:m,style:{marginTop:`12px`,background:`none`,border:`none`,color:`var(--color-primary, #666)`,fontSize:`13px`,cursor:`pointer`,textDecoration:`underline`,textUnderlineOffset:`3px`},children:m?`Launching…`:`Open browser first`}),c&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:c})]})})}function b({auth:e}){return e.appState===`loading`?(0,h.jsx)(`div`,{className:`connect-page`}):e.appState===`set-pin`?(0,h.jsx)(_,{pin:e.pin,setPin:e.setPin,showPin:e.showPin,setShowPin:e.setShowPin,pinLoading:e.pinLoading,pinError:e.pinError,pinInputRef:e.pinInputRef,setPinFormRef:e.setPinFormRef,onSubmit:e.handleSetPin,operatorName:e.operatorName,setOperatorName:e.setOperatorName}):e.appState===`connect-claude`?(0,h.jsx)(y,{authPolling:e.authPolling,setAuthPolling:e.setAuthPolling,authLoading:e.authLoading,setAuthLoading:e.setAuthLoading,pinError:e.pinError,setPinError:e.setPinError,setAppState:e.setAppState}):e.appState===`enter-pin`?(0,h.jsx)(v,{pin:e.pin,setPin:e.setPin,showPin:e.showPin,setShowPin:e.setShowPin,pinLoading:e.pinLoading,pinError:e.pinError,pinInputRef:e.pinInputRef,onSubmit:e.handleLogin,remoteAuthEnabled:e.remoteAuthEnabled,onSignOutRemote:()=>{console.info(`[admin-ui] remote-auth sign-out → /__remote-auth/logout`),window.location.href=`/__remote-auth/logout`}}):null}export{m as n,b as t};