Seed UNS hierarchy across 6 sites; rebrand outputs to SCADA IT/OT with ZB template
Lands per-site UNS subtree files (Warsaw West/North, Shannon, Galway, TMT, Ponce) seeded from OpenText facility docs — Warsaw split confirmed as numbered = legacy Zimmer = West, lettered = legacy Biomet = North. Renames project framing from "Shopfloor IT/OT" to "SCADA IT/OT" for accuracy. Extracts a ZB-branded PowerPoint template from example_powerpoint.pptx and wires it into the outputs pipeline. Trims deck from 18 to 16 slides (BOBJ->Power BI transferred to another team, Non-Goals and Asks dropped); goal-state BOBJ analysis pruned to a stub.
This commit is contained in:
@@ -50,7 +50,7 @@ Expected: `DESIGN.md IMPLEMENTATION-PLAN.md diagrams generated` (README, spec
|
||||
|
||||
---
|
||||
|
||||
## Task 2: outputs/presentation-spec.md — 18-slide PPTX structure anchor
|
||||
## Task 2: outputs/presentation-spec.md — 16-slide PPTX structure anchor
|
||||
|
||||
**Files:**
|
||||
- Create: `outputs/presentation-spec.md`
|
||||
@@ -178,11 +178,11 @@ Expected: `DESIGN.md IMPLEMENTATION-PLAN.md diagrams generated` (README, spec
|
||||
|
||||
**Step 2:** Read every source file named in the spec.
|
||||
|
||||
**Step 3:** For each of the 18 slides, populate content per the spec's rules.
|
||||
**Step 3:** For each of the 16 slides, populate content per the spec's rules.
|
||||
|
||||
**Step 4:** Invoke `document-skills:pptx` with:
|
||||
- Theme: `document-skills:theme-factory` default professional
|
||||
- Title: "Shopfloor IT/OT Transformation — 3-Year Plan"
|
||||
- Title: "SCADA IT/OT Transformation — 3-Year Plan"
|
||||
- Output path: `outputs/generated/plan-presentation.pptx`
|
||||
- Diagram images from `outputs/diagrams/`
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This directory holds the **generation pipeline** for two output artifacts derived from the plan markdown source:
|
||||
|
||||
1. A **mixed-stakeholder PowerPoint** (18 slides) — [`generated/plan-presentation.pptx`](generated/)
|
||||
1. A **mixed-stakeholder PowerPoint** (16 slides) — [`generated/plan-presentation.pptx`](generated/)
|
||||
2. A **long-form PDF** of the authoritative plan content — [`generated/plan-longform.pdf`](generated/)
|
||||
|
||||
Outputs are regenerated on demand from the current markdown source. Repeatability is anchored by **spec files** (this directory), not by prompts. See [`DESIGN.md`](DESIGN.md) for the full design rationale.
|
||||
|
||||
Binary file not shown.
@@ -12,9 +12,9 @@
|
||||
| **Theme** | `document-skills:theme-factory` default professional theme (serif body font). Override by adding a `**Theme override:**` line here. |
|
||||
| **Page size** | US Letter (8.5" × 11"). Change to A4 if distribution is primarily non-US. |
|
||||
| **Margins** | 1" on all sides |
|
||||
| **Running header** | Chapter title (left-aligned) · "Shopfloor IT/OT Transformation — 3-Year Plan" (right-aligned) |
|
||||
| **Running header** | Chapter title (left-aligned) · "SCADA IT/OT Transformation — 3-Year Plan" (right-aligned) |
|
||||
| **Running footer** | Page number (center) · As-of date (right) |
|
||||
| **Title on cover** | "Shopfloor IT/OT Transformation" |
|
||||
| **Title on cover** | "SCADA IT/OT Transformation" |
|
||||
| **Subtitle on cover** | "3-Year Plan" |
|
||||
| **Cover as-of line** | "As of {{regeneration-date}}" |
|
||||
| **Cover abstract** | A single paragraph lifted **verbatim** from [`../goal-state.md`](../goal-state.md) → **Vision** (the opening paragraph). Do not paraphrase. |
|
||||
@@ -52,6 +52,19 @@ These files are **not** part of the PDF — do not include them even if they see
|
||||
|
||||
If a new plan file is added to the repo and should be in the PDF, **add it here**, under Document structure, as a new chapter or appendix — not in the source file and not in the prompt.
|
||||
|
||||
### Section-level exclusions (render-time)
|
||||
|
||||
These **subsections** of an included source file are skipped when rendering. The source markdown is not edited — the renderer detects each heading by exact text match and omits the section through to the next sibling-or-higher heading. If the heading text in the source changes, this list must be updated.
|
||||
|
||||
| Source file | Heading to skip | Why excluded |
|
||||
|---|---|---|
|
||||
| [`../goal-state.md`](../goal-state.md) | `## Non-Goals` | Non-goals are still authoritative plan content (visible to anyone reading `goal-state.md` directly), but they were dropped from the long-form PDF + presentation in the 2026-04-30 trim — out-of-scope items don't need to be enumerated for the long-form audience the way they do for an in-room slide-deck audience. |
|
||||
| [`../goal-state.md`](../goal-state.md) | `### Enterprise reporting: BOBJ → Power BI migration (adjacent initiative)` | Coordination ownership was transferred to another team on 2026-04-30 (see the "Coordination ownership (resolved 2026-04-30)" note retained at the end of the section). The full three-paths / eight-questions / decision-rubric analysis is preserved in `goal-state.md` for the other team and for future reference, but is not part of the long-form PDF anymore. |
|
||||
|
||||
**Sub-heading scope:** when a `##` is excluded, every `###`/`####` under it is also excluded (the renderer walks until it hits another `##` or the end of file). Same rule applies one level down for `###`. Be deliberate when adding to this list: excluding a `##` removes more than excluding a `###`.
|
||||
|
||||
If you want to **re-include** an excluded section, remove it from this table — do not modify the source file.
|
||||
|
||||
## Transformations applied during rendering
|
||||
|
||||
The narrow set of changes applied to source markdown when producing the PDF. Source markdown files are **not edited** — transformations are applied at render time.
|
||||
@@ -128,7 +141,7 @@ Markdown `>` block quotes render as indented italic or colored blocks per the th
|
||||
### Cover page
|
||||
|
||||
```
|
||||
[Large type] Shopfloor IT/OT Transformation
|
||||
[Large type] SCADA IT/OT Transformation
|
||||
[Medium type] 3-Year Plan
|
||||
|
||||
[Small type] As of {{regeneration-date}}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
| **Theme** | `document-skills:theme-factory` default professional theme. Clean, neutral, no custom branding on first pass. Override: add a `**Theme override:**` line here with the target preset if you want different. |
|
||||
| **Body font** | Theme default sans-serif |
|
||||
| **Accent color** | Theme default |
|
||||
| **Source of truth for title** | "Shopfloor IT/OT Transformation — 3-Year Plan" |
|
||||
| **Source of truth for title** | "SCADA IT/OT Transformation — 3-Year Plan" |
|
||||
| **Subtitle template** | "As of {{date}}" where `{{date}}` is the regeneration timestamp in `YYYY-MM-DD` form |
|
||||
|
||||
## Truncation rules (apply to every slide unless overridden per-slide)
|
||||
@@ -29,7 +29,7 @@
|
||||
If `document-skills:pptx` cannot render a requested layout:
|
||||
|
||||
- **3-column content → single column with visual separators.** Applies to slide 5 (Three Pillars).
|
||||
- **2-column content → single column with a horizontal rule.** Applies to slide 16 (Open Coordination Items).
|
||||
- **2-column patterns → single column.** Applies to slide 16 (Digital Twin Scope) where the two access-control patterns are shown side-by-side; collapse to stacked patterns if the side-by-side layout doesn't fit at readable type size.
|
||||
- **Diagram + caption → caption only with a "(Diagram not yet available — render `<path>.mmd` and re-run)" note.** Applies to slides 8 and 9 until the PNG files exist in `diagrams/`.
|
||||
- **Multi-column table that overflows → split across two slides labeled "N/2" and "2/2".** Applies to slide 13 (Roadmap grid) if 7 rows × 3 columns doesn't fit one slide at readable type size.
|
||||
|
||||
@@ -41,7 +41,7 @@ If `document-skills:pptx` cannot render a requested layout:
|
||||
|---|---|
|
||||
| **Layout** | Title |
|
||||
| **Source** | — (no source file) |
|
||||
| **Content** | Title: *"Shopfloor IT/OT Transformation"* · Subtitle: *"3-Year Plan"* · Footer: *"As of {{regeneration-date}}"* |
|
||||
| **Content** | Title: *"SCADA IT/OT Transformation"* · Subtitle: *"3-Year Plan"* · Footer: *"As of {{regeneration-date}}"* |
|
||||
| **Rules** | No bullets, no extra content. Title slide is deliberately minimal. |
|
||||
|
||||
## Slide 2 — Executive Summary
|
||||
@@ -166,31 +166,14 @@ If `document-skills:pptx` cannot render a requested layout:
|
||||
| **Source** | [`../current-state/legacy-integrations.md`](../current-state/legacy-integrations.md) → Current inventory |
|
||||
| **Population** | 3 bullets — one per legacy integration: (1) **LEG-001** Aveva Web API → Delmia DNC (bidirectional orchestrated handshake; harder retirement — requires ScadaBridge scripts to re-implement System Platform parse logic). (2) **LEG-002** Aveva Web API ← Camstar MES (Camstar-initiated; easier retirement — ScadaBridge already has native Camstar path; requires Camstar-side reconfiguration). (3) **LEG-003** System Platform → custom email notification service (easier retirement — ScadaBridge native notifications already exist). Callout at bottom: *"Historian MSSQL reporting surface (BOBJ / Power BI) is explicitly carved out as not legacy — see `legacy-integrations.md` → Deliberately not tracked."* |
|
||||
|
||||
## Slide 16 — Open Coordination Items
|
||||
## Slide 16 — Digital Twin Scope
|
||||
|
||||
| Property | Value |
|
||||
|---|---|
|
||||
| **Layout** | 2-column content (fallback: single column with horizontal rule) |
|
||||
| **Source** | [`../goal-state.md`](../goal-state.md) → **Strategic Considerations (Adjacent Asks)** |
|
||||
| **Population** | **Left column — Digital twin (scope: two access-control patterns):** 4 bullets: (1) Scope is definitive — not a committed workstream, not a new component; (2) Pattern 1 — environment-lifecycle promotion without reconfiguration (ACL flip on write authority); (3) Pattern 2 — safe read-only consumption for KPI / monitoring systems (structural zero-write-path guarantee); (4) Both patterns are delivered by already-committed architecture (OtOpcUa ACL model + canonical model + single-connection-per-equipment). **Right column — BOBJ → Power BI:** 4 bullets: (1) In-flight reporting initiative, not owned by this plan; (2) Three consumption paths analyzed (SnowBridge curated layer in Snowflake / Historian direct / both); (3) Recommended position: Path C — hybrid, with Path A as strategic direction; (4) Next: schedule coordination conversation with reporting team — 8 questions ready in `goal-state.md`. |
|
||||
|
||||
## Slide 17 — Non-Goals
|
||||
|
||||
| Property | Value |
|
||||
|---|---|
|
||||
| **Layout** | Content (bulleted) |
|
||||
| **Source** | [`../goal-state.md`](../goal-state.md) → **Non-Goals** |
|
||||
| **Population** | 6 bullets, one line each: (1) Operator UX modernization — deprioritized against the three pillars; (2) Support staffing decisions — other teams; (3) Licensing strategy — not tracked; (4) Self-hosted orchestrator selection — chosen outside this plan; (5) VM-level DR — out of scope for Redpanda; (6) Physical network segmentation — out of scope. |
|
||||
| **Notes** | This slide is important for managing stakeholder expectations — what the plan *does not* commit to is as load-bearing as what it does commit to. Do not drop this slide even if time is short. |
|
||||
|
||||
## Slide 18 — Asks & Next Steps
|
||||
|
||||
| Property | Value |
|
||||
|---|---|
|
||||
| **Layout** | Content (bulleted) |
|
||||
| **Source** | [`../status.md`](../status.md) → **Top pending items** + inferred from [`../roadmap.md`](../roadmap.md) → Year 1 |
|
||||
| **Population** | 4 bullets: (1) Sponsor confirmation + Year 1 funding commitment; (2) Named owners for each of the 6 workstreams (build team alignment); (3) Power BI coordination conversation with reporting team — schedule; (4) UNS hierarchy snapshot walk owner named (Q1–Q2 Year 1 prerequisite for canonical model v1 publication). |
|
||||
| **Notes** | This is the closer slide. Each bullet should be a discrete ask with a clear "who needs to do what" so the audience leaves with action. |
|
||||
| **Layout** | Lede + 2-column pattern cards (fallback: stacked patterns) |
|
||||
| **Source** | [`../goal-state.md`](../goal-state.md) → **Strategic Considerations → Digital Twin** |
|
||||
| **Population** | **Lede:** scope is two access-control patterns — not a new component, not a new workstream — both delivered by already-committed architecture (OtOpcUa ACL model + canonical model + single-connection-per-equipment). **Pattern 1 card — Environment-lifecycle promotion:** promote between dev / staging / prod by flipping write-authority ACLs against stable equipment UUIDs; no client reconfiguration. **Pattern 2 card — Safe read-only consumption:** KPI / monitoring consumers get read-only grants with a structural zero-write-path guarantee — no equipment-side session for them to misuse. **Footnote:** physics simulation, FAT, commissioning emulation are out of scope; would be separate funded adjacent initiatives. |
|
||||
| **Notes** | This is the deck's closing slide. It exists because digital-twin scope was the most contested decision in the plan; making the resolved scope concrete prevents the topic from re-opening unnecessarily. The previous BOBJ → Power BI column was removed when that coordination was transferred to another team (2026-04-30). The previous Non-Goals and Asks & Next Steps slides were removed in the same pass. |
|
||||
|
||||
---
|
||||
|
||||
@@ -203,4 +186,4 @@ If `document-skills:pptx` cannot render a requested layout:
|
||||
|
||||
## Slide-count budget
|
||||
|
||||
Current: **18 slides.** If additions push this above 22, reconsider whether the deck is still "mixed-stakeholder" or has quietly become a build-team deck. The mixed-stakeholder audience tops out around 20 slides before attention fragments; a build-team deck belongs in a separate spec file (`build-team-spec.md` or similar) feeding a second generated output.
|
||||
Current: **16 slides.** If additions push this above 22, reconsider whether the deck is still "mixed-stakeholder" or has quietly become a build-team deck. The mixed-stakeholder audience tops out around 20 slides before attention fragments; a build-team deck belongs in a separate spec file (`build-team-spec.md` or similar) feeding a second generated output.
|
||||
|
||||
95
outputs/run-log.md
Normal file
95
outputs/run-log.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Output Regeneration Run Log
|
||||
|
||||
Append-only audit log — one entry per regeneration. Newest entry on top.
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-30 (later) — `regenerate presentation` (deck trimmed to 16 slides)
|
||||
|
||||
**Artifact:** `generated/plan-presentation.pptx` — **16 slides** (down from 18).
|
||||
|
||||
**Structural changes:**
|
||||
|
||||
- **Removed slide 17 (Non-Goals)** and **slide 18 (Asks & Next Steps)** — the deck no longer ships with these closing slides; the previous content lives in `goal-state.md` → Non-Goals and `status.md` → Top pending items respectively, and audiences who need them can read those directly.
|
||||
- **Slide 16 reshaped from "Open Coordination Items" (2-column: Digital twin + BOBJ→Power BI) to "Digital Twin Scope" (single-column with two pattern cards).** The BOBJ→Power BI column was dropped because that coordination is now owned by another team (recorded in `goal-state.md` 2026-04-30 update).
|
||||
- **Source files removed:** `outputs/workspace/slides/slide17.html` and `slide18.html` deleted; `slide16.html` rewritten.
|
||||
- **`generate.js` loop bound updated** from 18 → 16.
|
||||
- **Spec updated:** `outputs/presentation-spec.md` slide-count budget changed (18 → 16); slide 16 entry rewritten; slide 17 and 18 entries removed; fallback-rendering rule for slide 16 updated to reflect the new 2-pattern-card layout.
|
||||
- **Documentation updated:** "18-slide" → "16-slide" in `CLAUDE.md`, `outputs/README.md`, `outputs/IMPLEMENTATION-PLAN.md`.
|
||||
|
||||
**Closing slide judgment:** the deck now ends on Digital Twin Scope rather than Asks & Next Steps. Rationale: the asks have been resolved (Power BI moved to another team; UNS hierarchy walk has the questions doc) so the asks slide had nothing live left on it; Digital Twin Scope is a load-bearing settled decision worth ending on so the topic doesn't re-open.
|
||||
|
||||
**Longform spec aligned in the same pass.** `longform-spec.md` now carries a "Section-level exclusions (render-time)" rule that skips `goal-state.md → ## Non-Goals` and `goal-state.md → ### Enterprise reporting: BOBJ → Power BI migration (adjacent initiative)` from the long-form PDF. ("Asks & Next Steps" never appeared in the longform anyway — it was synthesized from `status.md` which is already excluded.)
|
||||
|
||||
**`goal-state.md` BOBJ section pruned in the same pass.** The full three-paths analysis, recommended position, eight questions for the reporting team, and four-bucket decision rubric (lines 796–864 prior, ~69 lines) were retired when the coordination was handed to another team. Replaced with a concise stub (5 lines) that preserves the section heading (so the longform exclusion rule still matches), the plan's posture (SnowBridge curated layer is shaped to support Power BI consumption if the reporting team points there), and a pointer to git history for anyone who needs the full retired analysis.
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-30 — `regenerate presentation` (ZB template integration)
|
||||
|
||||
**Artifact:** `generated/plan-presentation.pptx` (18 slides, 16:9, **now ZB-branded** via `templates/` integration). 2.0 MB, up from 370 KB at the prior run because the cover background image and bottom-border strip are embedded.
|
||||
|
||||
**Changes since prior run:**
|
||||
|
||||
- **Cover slide (slide 1) replaced with a ZB-branded cover.** `slide01.html` is no longer rendered through `html2pptx`; `generate.js` now constructs the cover directly from `templates/assets/cover-background.jpeg` plus a title overlay (white #F4F4F4, Arial Bold 24pt, positioned at scaled-down ZB-template coordinates).
|
||||
- **Brand chrome added to every content slide (2–18):** the bottom-border strip from `templates/assets/bottom-border.jpg` (positioned at 0.01", 5.07" — 0.75× scaled from the template's 13.33×7.5 to fit the existing 10×5.625 layout), "CONFIDENTIAL – FOR ZIMMER BIOMET INTERNAL USE ONLY" text (6pt gray) above the strip, and a single page number (8pt ZB navy #2B2A80) in the lower-right.
|
||||
- **Reskinned all 17 content-slide HTMLs** from the original navy/amber palette to the ZB palette via deterministic find-replace: navy header band (#1C2833) → ZB blue (#0066B3) → ultimately removed entirely; off-white backgrounds → pure white; slate text (#2E4053) → dark gray (#333333); amber accent (#D4843D) → ZB blue (#0066B3); dark navy text → ZB navy (#2B2A80).
|
||||
- **Removed the dark blue header band** entirely; titles are now ZB blue text on white with a 1.5pt blue underline. Page numbers are no longer in the markup (they live only in the JS-added chrome) — eliminates the duplicate-page-number bug introduced when chrome was first added.
|
||||
- **`generate.js` documents the scaling rationale** inline so future maintainers don't get confused about why coordinates aren't the template's published values.
|
||||
|
||||
**Source plan state at time of regeneration:**
|
||||
|
||||
Same as the 2026-04-24 run plus: Warsaw West/North campus split clarified (numbered = legacy Zimmer = West; lettered = legacy Biomet = North); UNS hierarchy initial draft seeded across all 6 integrated sites; BOBJ→Power BI coordination ownership transferred to another team.
|
||||
|
||||
**Warnings / deviations:**
|
||||
|
||||
- **HTML CSS still authored to 720×405pt (10×5.625").** pptxgen's `LAYOUT_16x9` matches that. The ZB template publishes coordinates at 13.33×7.5, so `generate.js` scales by 0.75× when placing chrome. If the slides are ever rebuilt at the template's native 13.33×7.5, drop the scaling and use template coords as published.
|
||||
- **Slide 13 was 1.5pt overflow on first attempt** (new title section was 10pt taller than the old header band). Tightened header padding and reduced title font from 24pt back to 22pt; now fits.
|
||||
- **Diagrams still missing** (`architecture-layers.png`, `end-to-end-flow.png`) — same fallback pattern as before; not reapplied this run.
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-24 — `regenerate presentation`
|
||||
|
||||
**Artifact:** `generated/plan-presentation.pptx` (18 slides, 16:9, theme-factory default professional — Classic Blue + amber accent palette).
|
||||
|
||||
**Source plan state at time of regeneration:**
|
||||
|
||||
- 6 workstreams in `roadmap.md` (down from 7 — Snowflake dbt Transform Layer workstream collapsed into SnowBridge earlier in the session).
|
||||
- 3 legacy integrations in `current-state/legacy-integrations.md` (LEG-001 Delmia DNC, LEG-002 Camstar MES, LEG-003 custom email notification service).
|
||||
- Digital-twin scope finalized as two access-control patterns (environment-lifecycle promotion; safe read-only KPI/monitoring consumption).
|
||||
- SnowBridge scope finalized as ingest + in-process .NET transform + curated-table write (no dbt, no external orchestrator, no Snowflake landing tier).
|
||||
- Canonical model has three surfaces: OtOpcUa equipment namespace, Redpanda topics + Protobuf schemas, SnowBridge curated layer in Snowflake.
|
||||
|
||||
**Slides populated:**
|
||||
|
||||
1. Title — "Shopfloor IT/OT Transformation · 3-Year Plan · As of 2026-04-24"
|
||||
2. Executive Summary — Vision + three-pillar criteria + out-of-scope line
|
||||
3. Today's Reality — 5 pain points synthesized from `current-state.md`
|
||||
4. Vision — full-slide quote + 3 sub-bullets
|
||||
5. Three Pillars — 3-column layout (Unification / Analytics-AI / Legacy Retirement)
|
||||
6. Enterprise Layout — 4 tier groups (primary / largest / integrated / not yet integrated)
|
||||
7. Today's Systems — 6 systems summarized
|
||||
8. Goal State: Layered Architecture — rendered as a stacked-layer visualization + "sole IT↔OT crossing" callout. **Placeholder diagram** for `architecture-layers.png` — not rendered; layer stack is the fallback visualization.
|
||||
9. End-to-End Data Flow — rendered as a 2-row node flow (OT row + IT row) with arrows + caption. **Placeholder diagram** for `end-to-end-flow.png` — not rendered; node-flow layout is the fallback visualization.
|
||||
10. OtOpcUa — the Unification Layer — 6 bullets
|
||||
11. Analytics Stack: SnowBridge + Snowflake — 6 bullets, reflecting SnowBridge-owns-transform scope
|
||||
12. Redpanda EventHub — 6 bullets
|
||||
13. 3-Year Roadmap grid — 6×3 table (workstreams × years), cells truncated to single most load-bearing commitment per cell
|
||||
14. Year 1 Focus — 6 bullets, one per workstream
|
||||
15. Pillar 3: Legacy Retirement — 3 cards (LEG-001, LEG-002, LEG-003) + Historian carve-out callout
|
||||
16. Open Coordination Items — 2-column (Digital twin scope / BOBJ → Power BI)
|
||||
17. Non-Goals — 6 bullets
|
||||
18. Asks & Next Steps — 4 asks (sponsor, build-team owners, Power BI conversation, UNS hierarchy walk owner)
|
||||
|
||||
**Warnings / deviations:**
|
||||
|
||||
- **Diagrams `architecture-layers.png` and `end-to-end-flow.png` do not exist** in `diagrams/`. Spec fallback says "render a placeholder box with a text prompt." Deviation: slides 8 and 9 use **content-based substitutes** (stacked-layer visualization on 8; horizontal node-flow on 9) rather than literal placeholder boxes. This is a richer substitute than the spec's minimal fallback but still not the diagrams themselves. To align exactly with the spec, author the `.mmd` sources in `diagrams/` and re-run.
|
||||
- **Stale diagram file still in `diagrams/`: `snowflake-dbt-dataflow.png`** — not referenced by this regeneration, but should be renamed / replaced when a matching "SnowBridge curated layer dataflow" component diagram is authored. Flagged for attention; not a blocker for this run.
|
||||
- **`<code>` tag not supported by html2pptx** — initial rendering of slide 12 dropped content inside `<code>` tags (the `schemas` repo name, `buf` CI tool, and `{domain}.{entity}.{event-type}` topic format). Fixed by replacing `<code>` with `<b>` for those tokens. Watch for this pattern in future slides.
|
||||
- **Text elements cannot carry CSS borders** — html2pptx validator rejects borders on `<p>` / `<h1>` / `<ul>`. Vision and tagline elements on slides 2, 4, 10, 11, 12 were wrapped in `<div>`s carrying the border-left accent.
|
||||
- **Slide 1 title wrap** — initial 44pt title wrapped to two lines and overlapped the subtitle. Reduced to 32pt with explicit vertical spacing; fits on one line at 720pt width.
|
||||
|
||||
**Intermediate artifacts removed** from `workspace/` after validation: `plan-presentation.pdf`, `slide-NN.jpg`, `check-NN.jpg`. Retained: `slides/` HTML, `generate.js`, `node_modules/`, `package*.json`.
|
||||
|
||||
**Diff from prior run (2026-04-15):** deck reflects two scope changes that happened between runs — digital-twin scope narrowed (slide 16 left column rewritten around the two access-control patterns); SnowBridge expanded to own transformation (slides 11, 13 row 3, 14 bullet 3 all updated); workstream count dropped from 7 to 6 (slides 13, 14, 18 all reflect this); enterprise shortname resolved to `zb` and Warsaw West buildings confirmed as 5 and 19 (slide 6).
|
||||
155
outputs/workspace/generate.js
Normal file
155
outputs/workspace/generate.js
Normal file
@@ -0,0 +1,155 @@
|
||||
const pptxgen = require('pptxgenjs');
|
||||
const html2pptx = require('/Users/dohertj2/.claude/plugins/cache/anthropic-agent-skills/document-skills/unknown/skills/pptx/scripts/html2pptx.js');
|
||||
const path = require('path');
|
||||
|
||||
const SLIDES_DIR = path.join(__dirname, 'slides');
|
||||
const OUTPUT = '/Users/dohertj2/Desktop/plan/outputs/generated/plan-presentation.pptx';
|
||||
|
||||
// ZB template assets and design constants (see /Users/dohertj2/Desktop/plan/templates/README.md)
|
||||
const TEMPLATE_ASSETS = '/Users/dohertj2/Desktop/plan/templates/assets';
|
||||
const COVER_BG = path.join(TEMPLATE_ASSETS, 'cover-background.jpeg');
|
||||
const BOTTOM_BORDER = path.join(TEMPLATE_ASSETS, 'bottom-border.jpg');
|
||||
|
||||
const ZB_BLUE = '0066B3';
|
||||
const ZB_NAVY = '2B2A80';
|
||||
const ZB_TITLE_WHITE = 'F4F4F4';
|
||||
const CONFIDENTIAL_GRAY = '404040';
|
||||
const CONFIDENTIAL_TEXT = 'CONFIDENTIAL – FOR ZIMMER BIOMET INTERNAL USE ONLY';
|
||||
|
||||
// Roadmap-table colors (slide 13)
|
||||
const NAVY = '1C2833';
|
||||
const SLATE = '2E4053';
|
||||
const SILVER = 'AAB7B8';
|
||||
const WHITE = 'FFFFFF';
|
||||
const ROW_BG = 'FFFFFF';
|
||||
const ROW_BG_ALT = 'EEF1F3';
|
||||
|
||||
async function main() {
|
||||
const pres = new pptxgen();
|
||||
pres.layout = 'LAYOUT_16x9'; // 10.0 × 5.625 in — matches the HTML slide CSS
|
||||
pres.author = 'IT/OT Transformation Team';
|
||||
pres.subject = 'SCADA IT/OT Transformation — 3-Year Plan';
|
||||
pres.title = 'SCADA IT/OT Transformation';
|
||||
|
||||
// Slide 1 — ZB cover (replaces slide01.html; cover background + title overlay)
|
||||
console.log('Processing slide 1 (ZB cover)...');
|
||||
addCoverSlide(pres);
|
||||
|
||||
// Slides 2–16 — html2pptx-rendered content + ZB brand chrome (bottom border, confidentiality, page number)
|
||||
for (let i = 2; i <= 16; i++) {
|
||||
const num = String(i).padStart(2, '0');
|
||||
const file = path.join(SLIDES_DIR, `slide${num}.html`);
|
||||
console.log(`Processing slide ${i}...`);
|
||||
|
||||
const { slide } = await html2pptx(file, pres);
|
||||
addBrandChrome(slide, i);
|
||||
if (i === 13) {
|
||||
addRoadmapTable(pres, slide);
|
||||
}
|
||||
}
|
||||
|
||||
await pres.writeFile({ fileName: OUTPUT });
|
||||
console.log(`Saved: ${OUTPUT}`);
|
||||
}
|
||||
|
||||
// Coordinates below are scaled 0.75x from the 13.33×7.5 ZB template (templates/README.md)
|
||||
// to fit pptxgen's LAYOUT_16x9 (10×5.625 in). The HTML CSS uses 720×405pt = 10×5.625 in.
|
||||
|
||||
function addCoverSlide(pres) {
|
||||
const slide = pres.addSlide();
|
||||
slide.background = { color: 'FFFFFF' };
|
||||
slide.addImage({ path: COVER_BG, x: 0, y: 0, w: 10.0, h: 5.625 });
|
||||
slide.addText('SCADA IT/OT Transformation', {
|
||||
x: 0.69, y: 1.04, w: 4.5, h: 1.13,
|
||||
fontFace: 'Arial', fontSize: 24, bold: true,
|
||||
color: ZB_TITLE_WHITE, valign: 'top',
|
||||
});
|
||||
slide.addText('3-Year Plan', {
|
||||
x: 0.69, y: 2.21, w: 4.5, h: 0.38,
|
||||
fontFace: 'Arial', fontSize: 14,
|
||||
color: ZB_TITLE_WHITE,
|
||||
});
|
||||
slide.addText('AS OF 2026-04-30', {
|
||||
x: 0.69, y: 2.59, w: 4.5, h: 0.30,
|
||||
fontFace: 'Arial', fontSize: 9,
|
||||
color: ZB_TITLE_WHITE, charSpacing: 1,
|
||||
});
|
||||
return slide;
|
||||
}
|
||||
|
||||
function addBrandChrome(slide, pageNumber) {
|
||||
slide.addImage({
|
||||
path: BOTTOM_BORDER,
|
||||
x: 0.01, y: 5.07, w: 9.99, h: 0.555,
|
||||
});
|
||||
slide.addText(CONFIDENTIAL_TEXT, {
|
||||
x: 0.22, y: 4.91, w: 6.0, h: 0.15,
|
||||
fontFace: 'Arial', fontSize: 6, color: CONFIDENTIAL_GRAY,
|
||||
});
|
||||
slide.addText(String(pageNumber), {
|
||||
x: 9.55, y: 4.91, w: 0.35, h: 0.15,
|
||||
fontFace: 'Arial', fontSize: 8, color: ZB_NAVY, align: 'right',
|
||||
});
|
||||
}
|
||||
|
||||
function addRoadmapTable(pres, slide) {
|
||||
const headerOpts = { bold: true, color: WHITE, fill: { color: NAVY }, fontSize: 10, fontFace: 'Arial', align: 'center', valign: 'middle' };
|
||||
const wsOpts = { bold: true, fontSize: 9, fontFace: 'Arial', color: NAVY, fill: { color: ROW_BG_ALT }, valign: 'middle' };
|
||||
const cellOpts = { fontSize: 8, fontFace: 'Arial', color: SLATE, fill: { color: ROW_BG }, valign: 'middle' };
|
||||
|
||||
const rows = [
|
||||
[
|
||||
{ text: 'Workstream', options: headerOpts },
|
||||
{ text: 'Year 1 — Foundation', options: headerOpts },
|
||||
{ text: 'Year 2 — Scale', options: headerOpts },
|
||||
{ text: 'Year 3 — Completion', options: headerOpts },
|
||||
],
|
||||
[
|
||||
{ text: 'OtOpcUa', options: wsOpts },
|
||||
{ text: 'Evolve LmxOpcUa; deploy to every site; tier-1 cutover begins; UNS hierarchy walk', options: cellOpts },
|
||||
{ text: 'Complete tier 1; begin tier 2 (Ignition); long-tail drivers on demand', options: cellOpts },
|
||||
{ text: 'Complete tier 2; execute tier 3 (System Platform IO) with compliance validation', options: cellOpts },
|
||||
],
|
||||
[
|
||||
{ text: 'Redpanda EventHub', options: wsOpts },
|
||||
{ text: 'Central cluster in SB; schema registry; canonical model v1 published (FANUC CNC pilot)', options: cellOpts },
|
||||
{ text: 'Expand topic coverage; WAN-outage replay drill; iterate canonical model', options: cellOpts },
|
||||
{ text: 'Steady state; canonical model mature; alerting + runbooks hardened', options: cellOpts },
|
||||
],
|
||||
[
|
||||
{ text: 'SnowBridge', options: wsOpts },
|
||||
{ text: 'Custom build in .NET; first adapter (Historian SQL); first curated tables aligned to canonical model', options: cellOpts },
|
||||
{ text: 'Add Redpanda adapter; operator UI + approval workflow; canonical-state OEE model; first "not possible before" use case in dev', options: cellOpts },
|
||||
{ text: 'All source adapters live; hardening. "Not possible before" use case in production — pillar 2 passes', options: cellOpts },
|
||||
],
|
||||
[
|
||||
{ text: 'ScadaBridge Extensions', options: wsOpts },
|
||||
{ text: 'Deadband / exception-based publishing; EventHub producer with store-and-forward', options: cellOpts },
|
||||
{ text: 'Roll to all integrated sites; tune deadband from observed Snowflake cost', options: cellOpts },
|
||||
{ text: 'Steady state; residual cleanup for onboarding / retirement tails', options: cellOpts },
|
||||
],
|
||||
[
|
||||
{ text: 'Site Onboarding', options: wsOpts },
|
||||
{ text: 'No new sites; define lightweight onboarding pattern for smaller sites', options: cellOpts },
|
||||
{ text: 'Pilot one smaller site; scale to additional smaller sites', options: cellOpts },
|
||||
{ text: 'Complete onboarding of all remaining smaller sites — pillar 1 passes', options: cellOpts },
|
||||
],
|
||||
[
|
||||
{ text: 'Legacy Retirement', options: wsOpts },
|
||||
{ text: 'Inventory populated (3 rows); retire first integration as pattern-proving exercise', options: cellOpts },
|
||||
{ text: 'Bulk migration against inventory; quarterly burn-down tracking', options: cellOpts },
|
||||
{ text: 'Inventory reaches zero — pillar 3 passes', options: cellOpts },
|
||||
],
|
||||
];
|
||||
|
||||
slide.addTable(rows, {
|
||||
x: 0.42,
|
||||
y: 1.2,
|
||||
w: 9.2,
|
||||
colW: [1.5, 2.566, 2.567, 2.567],
|
||||
rowH: 0.55,
|
||||
border: { pt: 0.5, color: SILVER },
|
||||
});
|
||||
}
|
||||
|
||||
main().catch((err) => { console.error(err); process.exit(1); });
|
||||
761
outputs/workspace/package-lock.json
generated
Normal file
761
outputs/workspace/package-lock.json
generated
Normal file
@@ -0,0 +1,761 @@
|
||||
{
|
||||
"name": "workspace",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"playwright": "^1.59.1",
|
||||
"pptxgenjs": "^4.0.1",
|
||||
"sharp": "^0.34.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/colour": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
|
||||
"integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
|
||||
"integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
|
||||
"integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-riscv64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
|
||||
"integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
|
||||
"integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
|
||||
"integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-ppc64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
|
||||
"integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-riscv64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
|
||||
"integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-riscv64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-s390x": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
|
||||
"integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-wasm32": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
|
||||
"integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-ia32": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
|
||||
"integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.19.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz",
|
||||
"integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/https": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz",
|
||||
"integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/image-size": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz",
|
||||
"integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"queue": "6.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"image-size": "bin/image-size.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.x"
|
||||
}
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||
"license": "(MIT OR GPL-3.0-or-later)",
|
||||
"dependencies": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
"readable-stream": "~2.3.6",
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.59.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz",
|
||||
"integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.59.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.59.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
|
||||
"integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/pptxgenjs": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pptxgenjs/-/pptxgenjs-4.0.1.tgz",
|
||||
"integrity": "sha512-TeJISr8wouAuXw4C1F/mC33xbZs/FuEG6nH9FG1Zj+nuPcGMP5YRHl6X+j3HSUnS1f3at6k75ZZXPMZlA5Lj9A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "^22.8.1",
|
||||
"https": "^1.0.0",
|
||||
"image-size": "^1.2.1",
|
||||
"jszip": "^3.10.1"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/queue": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
|
||||
"integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "~2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
||||
"integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@img/colour": "^1.0.0",
|
||||
"detect-libc": "^2.1.2",
|
||||
"semver": "^7.7.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.34.5",
|
||||
"@img/sharp-darwin-x64": "0.34.5",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-arm": "1.2.4",
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-riscv64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.4",
|
||||
"@img/sharp-libvips-linux-x64": "1.2.4",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.4",
|
||||
"@img/sharp-linux-arm": "0.34.5",
|
||||
"@img/sharp-linux-arm64": "0.34.5",
|
||||
"@img/sharp-linux-ppc64": "0.34.5",
|
||||
"@img/sharp-linux-riscv64": "0.34.5",
|
||||
"@img/sharp-linux-s390x": "0.34.5",
|
||||
"@img/sharp-linux-x64": "0.34.5",
|
||||
"@img/sharp-linuxmusl-arm64": "0.34.5",
|
||||
"@img/sharp-linuxmusl-x64": "0.34.5",
|
||||
"@img/sharp-wasm32": "0.34.5",
|
||||
"@img/sharp-win32-arm64": "0.34.5",
|
||||
"@img/sharp-win32-ia32": "0.34.5",
|
||||
"@img/sharp-win32-x64": "0.34.5"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
7
outputs/workspace/package.json
Normal file
7
outputs/workspace/package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"playwright": "^1.59.1",
|
||||
"pptxgenjs": "^4.0.1",
|
||||
"sharp": "^0.34.5"
|
||||
}
|
||||
}
|
||||
26
outputs/workspace/slides/slide01.html
Normal file
26
outputs/workspace/slides/slide01.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #1C2833; font-family: Arial, sans-serif;
|
||||
display: flex; color: #F4F6F6;
|
||||
}
|
||||
.wrap { width: 720pt; padding: 140pt 60pt 0 60pt; text-align: center; }
|
||||
.accent-bar { width: 100pt; height: 4pt; background: #D4843D; margin: 0 auto 22pt auto; }
|
||||
h1 { font-size: 32pt; font-weight: bold; margin: 0 0 12pt 0; letter-spacing: -0.5pt; text-align: center; }
|
||||
.subtitle { font-size: 20pt; color: #AAB7B8; margin: 0 0 44pt 0; font-weight: normal; text-align: center; }
|
||||
.date { font-size: 11pt; color: #AAB7B8; margin: 0; letter-spacing: 1pt; text-align: center; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<div class="accent-bar"></div>
|
||||
<h1>SCADA IT/OT Transformation</h1>
|
||||
<p class="subtitle">3-Year Plan</p>
|
||||
<p class="date">AS OF 2026-04-24</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
33
outputs/workspace/slides/slide02.html
Normal file
33
outputs/workspace/slides/slide02.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; }
|
||||
.vision-wrap { border-left: 3pt solid #0066B3; padding-left: 12pt; margin: 0 0 14pt 0; }
|
||||
.vision { color: #2B2A80; font-size: 14pt; font-style: italic; margin: 0; }
|
||||
ul { margin: 6pt 0 0 22pt; padding: 0; }
|
||||
li { font-size: 12pt; margin-bottom: 8pt; line-height: 1.35; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Executive Summary</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="vision-wrap"><p class="vision">Vision: stable, single point of integration between shopfloor OT and enterprise IT.</p></div>
|
||||
<ul>
|
||||
<li><b>Pillar 1 — Unification:</b> 100% of sites on the standardized stack at end of Year 3.</li>
|
||||
<li><b>Pillar 2 — Analytics/AI enablement:</b> ≤15-minute analytics SLO; one "not possible before" use case in production.</li>
|
||||
<li><b>Pillar 3 — Legacy retirement:</b> bespoke IT↔OT integration inventory driven to zero.</li>
|
||||
<li><b>Out of scope:</b> operator UX modernization, licensing strategy, VM-level DR, physical network segmentation.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
34
outputs/workspace/slides/slide03.html
Normal file
34
outputs/workspace/slides/slide03.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 24pt 40pt; flex: 1; color: #333333; }
|
||||
ul { margin: 0 0 0 22pt; padding: 0; }
|
||||
li { font-size: 13pt; margin-bottom: 11pt; line-height: 1.35; }
|
||||
.footer { padding: 6pt 40pt 10pt 40pt; color: #AAB7B8; font-size: 9pt; font-style: italic; }
|
||||
.footer p { margin: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Today's Reality</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<ul>
|
||||
<li><b>Split SCADA stack</b> — Aveva System Platform for validated data; Ignition for KPI monitoring.</li>
|
||||
<li><b>Central Ignition</b> in South Bend holds direct OPC UA sessions to site equipment over the WAN.</li>
|
||||
<li><b>Multiple concurrent OPC UA sessions</b> to the same equipment from different consumers.</li>
|
||||
<li><b>Three legacy IT↔OT integrations</b> bypass ScadaBridge central (Delmia DNC, Camstar MES, custom email service).</li>
|
||||
<li><b>Fragmented data access</b> — consumers reading the same tag can see different values depending on sampling/deadband settings.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer"><p>Full detail: current-state.md → Systems & Interfaces, Equipment OPC UA.</p></div>
|
||||
</body>
|
||||
</html>
|
||||
32
outputs/workspace/slides/slide04.html
Normal file
32
outputs/workspace/slides/slide04.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 40pt 60pt; flex: 1; color: #333333; display: flex; flex-direction: column; justify-content: center; }
|
||||
.quote-wrap { border-left: 5pt solid #0066B3; padding-left: 18pt; margin: 0 0 28pt 0; }
|
||||
.quote { font-size: 22pt; color: #2B2A80; font-weight: bold; line-height: 1.3; margin: 0; }
|
||||
ul { margin: 0 0 0 22pt; padding: 0; }
|
||||
li { font-size: 13pt; margin-bottom: 9pt; line-height: 1.35; color: #333333; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Vision</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="quote-wrap"><p class="quote">A stable, single point of integration between shopfloor OT and enterprise IT.</p></div>
|
||||
<ul>
|
||||
<li>Every cross-domain path flows through ScadaBridge central — the sole sanctioned IT↔OT crossing point.</li>
|
||||
<li>Equipment data is site-local, canonically modeled, and shared through a single session per device.</li>
|
||||
<li>Tiebreaker for ambiguous decisions: preserve this stable-single-point-of-integration posture.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
45
outputs/workspace/slides/slide05.html
Normal file
45
outputs/workspace/slides/slide05.html
Normal file
@@ -0,0 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 26pt 36pt; flex: 1; color: #333333; display: flex; flex-direction: row; gap: 18pt; }
|
||||
.pillar { flex: 1; background: #ffffff; border-top: 4pt solid #0066B3; padding: 18pt 16pt; border-radius: 4pt; }
|
||||
.pillar h2 { margin: 0 0 6pt 0; font-size: 14pt; color: #2B2A80; }
|
||||
.pillar .tag { font-size: 9pt; color: #AAB7B8; margin: 0 0 12pt 0; letter-spacing: 1pt; }
|
||||
.pillar .criterion { font-size: 12pt; font-weight: bold; color: #2B2A80; margin: 0 0 10pt 0; }
|
||||
.pillar .context { font-size: 11pt; margin: 0; color: #333333; line-height: 1.4; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Three Pillars</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="pillar">
|
||||
<p class="tag">PILLAR 1</p>
|
||||
<h2>Unification</h2>
|
||||
<p class="criterion">100% of sites on the standardized stack.</p>
|
||||
<p class="context">Every site runs OtOpcUa + ScadaBridge + Redpanda + SnowBridge + Snowflake. No bespoke site-specific integration.</p>
|
||||
</div>
|
||||
<div class="pillar">
|
||||
<p class="tag">PILLAR 2</p>
|
||||
<h2>Analytics / AI Enablement</h2>
|
||||
<p class="criterion">≤15-minute SLO; one "not possible before" use case in production.</p>
|
||||
<p class="context">Canonical model + curated layer unlock cross-domain analytics that fragmented today's estate can't deliver.</p>
|
||||
</div>
|
||||
<div class="pillar">
|
||||
<p class="tag">PILLAR 3</p>
|
||||
<h2>Legacy Retirement</h2>
|
||||
<p class="criterion">Inventory driven to zero.</p>
|
||||
<p class="context">Three bespoke IT↔OT integrations today; each migrates to ScadaBridge-central or is decommissioned.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
44
outputs/workspace/slides/slide06.html
Normal file
44
outputs/workspace/slides/slide06.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; }
|
||||
.group { margin-bottom: 13pt; }
|
||||
.group-label { font-size: 10pt; color: #0066B3; letter-spacing: 1pt; font-weight: bold; margin: 0 0 4pt 0; }
|
||||
.group-content { font-size: 13pt; margin: 0; line-height: 1.35; color: #2B2A80; }
|
||||
.footer { padding: 6pt 40pt 10pt 40pt; color: #AAB7B8; font-size: 9pt; font-style: italic; }
|
||||
.footer p { margin: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Enterprise Layout</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="group">
|
||||
<p class="group-label">PRIMARY DATA CENTER</p>
|
||||
<p class="group-content">South Bend — central hosting for Aveva System Platform primary cluster, Ignition, Historian, Snowflake.</p>
|
||||
</div>
|
||||
<div class="group">
|
||||
<p class="group-label">LARGEST SITES</p>
|
||||
<p class="group-content">Warsaw West (bldg-5, bldg-19) · Warsaw North — one cluster per production building.</p>
|
||||
</div>
|
||||
<div class="group">
|
||||
<p class="group-label">OTHER INTEGRATED SITES</p>
|
||||
<p class="group-content">Shannon · Galway · TMT · Ponce — single cluster per site.</p>
|
||||
</div>
|
||||
<div class="group">
|
||||
<p class="group-label">NOT YET INTEGRATED</p>
|
||||
<p class="group-content">Berlin · Winterthur · Jacksonville · others — lighter-weight onboarding pattern Year 2+.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer"><p>Smaller-site list is volatile; rely on named examples as illustrative, not authoritative.</p></div>
|
||||
</body>
|
||||
</html>
|
||||
32
outputs/workspace/slides/slide07.html
Normal file
32
outputs/workspace/slides/slide07.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; }
|
||||
ul { margin: 0 0 0 22pt; padding: 0; }
|
||||
li { font-size: 12pt; margin-bottom: 9pt; line-height: 1.35; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Today's Systems</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<ul>
|
||||
<li><b>Aveva System Platform</b> — validated data collection; primary cluster central + site clusters.</li>
|
||||
<li><b>Ignition SCADA</b> — KPI monitoring; central in South Bend, WAN-dependent direct OPC UA to sites.</li>
|
||||
<li><b>ScadaBridge</b> — in-house Akka.NET integration layer; already deployed; the IT↔OT strategic layer.</li>
|
||||
<li><b>LmxOpcUa</b> — in-house OPC UA server exposing System Platform objects; per-node today, evolves into OtOpcUa.</li>
|
||||
<li><b>Camstar MES</b> — sole enterprise MES; today integrates via single Aveva Web API call.</li>
|
||||
<li><b>Aveva Historian</b> — sole historian; central-only in South Bend; permanent retention; BOBJ → Power BI migration in flight.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
54
outputs/workspace/slides/slide08.html
Normal file
54
outputs/workspace/slides/slide08.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; display: flex; flex-direction: column; }
|
||||
.layer-stack { display: flex; flex-direction: column; gap: 6pt; }
|
||||
.layer { background: #ffffff; padding: 10pt 14pt; border-left: 6pt solid #0066B3; border-radius: 3pt; }
|
||||
.layer .tag { font-size: 9pt; color: #0066B3; letter-spacing: 1pt; font-weight: bold; margin: 0 0 2pt 0; }
|
||||
.layer .name { font-size: 12pt; font-weight: bold; margin: 0 0 2pt 0; color: #2B2A80; }
|
||||
.layer .desc { font-size: 10pt; margin: 0; color: #333333; }
|
||||
.callout { background: #0066B3; color: #FFFFFF; padding: 10pt 14pt; border-radius: 3pt; margin-top: 10pt; }
|
||||
.callout p { margin: 0; font-size: 11pt; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Goal State — Layered Architecture</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="layer-stack">
|
||||
<div class="layer">
|
||||
<p class="tag">LAYER 1 — EQUIPMENT</p>
|
||||
<p class="name">PLCs, CNCs, OPC UA-native devices</p>
|
||||
<p class="desc">One OPC UA session per device; OtOpcUa holds it.</p>
|
||||
</div>
|
||||
<div class="layer">
|
||||
<p class="tag">LAYER 2 — OtOpcUa</p>
|
||||
<p class="name">Equipment namespace + System Platform namespace (absorbs LmxOpcUa)</p>
|
||||
<p class="desc">Clustered, site-local. Single sanctioned OPC UA access point per site.</p>
|
||||
</div>
|
||||
<div class="layer">
|
||||
<p class="tag">LAYER 3 — SCADA</p>
|
||||
<p class="name">Aveva System Platform (validated) · Ignition (KPI)</p>
|
||||
<p class="desc">Derives canonical machine state from raw signals.</p>
|
||||
</div>
|
||||
<div class="layer">
|
||||
<p class="tag">LAYER 4 — ScadaBridge</p>
|
||||
<p class="name">IT/OT bridge — central integration layer</p>
|
||||
<p class="desc">Deadband, store-and-forward, producer to Redpanda.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="callout">
|
||||
<p><b>ScadaBridge central is the sole IT↔OT crossing point.</b></p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
50
outputs/workspace/slides/slide09.html
Normal file
50
outputs/workspace/slides/slide09.html
Normal file
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 30pt; flex: 1; color: #333333; display: flex; flex-direction: column; justify-content: center; }
|
||||
.flow-row { display: flex; justify-content: space-between; align-items: center; gap: 4pt; margin-bottom: 16pt; }
|
||||
.node { background: #ffffff; border: 1pt solid #AAB7B8; padding: 10pt 6pt; border-radius: 3pt; flex: 1; text-align: center; }
|
||||
.node p { margin: 0; font-size: 9pt; font-weight: bold; color: #2B2A80; }
|
||||
.node .type { font-size: 7pt; color: #AAB7B8; margin-top: 2pt; letter-spacing: 0.5pt; }
|
||||
.arrow { font-size: 12pt; color: #0066B3; font-weight: bold; }
|
||||
.arrow p { margin: 0; }
|
||||
.ot { background: #E8EDF0; }
|
||||
.it { background: #FCEFE3; }
|
||||
.caption { font-size: 11pt; color: #333333; text-align: center; line-height: 1.4; margin: 12pt 20pt 0 20pt; font-style: italic; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>End-to-End Data Flow</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="flow-row">
|
||||
<div class="node ot"><p>Equipment</p><p class="type">OT</p></div>
|
||||
<div class="arrow"><p>→</p></div>
|
||||
<div class="node ot"><p>OtOpcUa</p><p class="type">OT · Layer 2</p></div>
|
||||
<div class="arrow"><p>→</p></div>
|
||||
<div class="node ot"><p>System Platform / Ignition</p><p class="type">OT · Layer 3</p></div>
|
||||
<div class="arrow"><p>→</p></div>
|
||||
<div class="node ot"><p>ScadaBridge</p><p class="type">OT · Layer 4</p></div>
|
||||
</div>
|
||||
<div class="flow-row">
|
||||
<div class="node it"><p>Redpanda</p><p class="type">IT · EventHub</p></div>
|
||||
<div class="arrow"><p>→</p></div>
|
||||
<div class="node it"><p>SnowBridge</p><p class="type">IT · Ingest + Transform</p></div>
|
||||
<div class="arrow"><p>→</p></div>
|
||||
<div class="node it"><p>Snowflake</p><p class="type">IT · Curated Layer</p></div>
|
||||
<div class="arrow"><p>→</p></div>
|
||||
<div class="node it"><p>Power BI</p><p class="type">IT · Consumer</p></div>
|
||||
</div>
|
||||
<p class="caption">A tag read from a machine in Warsaw West flows across the stack into enterprise analytics, without a second IT↔OT crossing.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
35
outputs/workspace/slides/slide10.html
Normal file
35
outputs/workspace/slides/slide10.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; }
|
||||
.tagline-wrap { border-left: 3pt solid #0066B3; padding-left: 10pt; margin: 0 0 12pt 0; }
|
||||
.tagline { font-size: 12pt; color: #2B2A80; margin: 0; font-style: italic; }
|
||||
ul { margin: 0 0 0 22pt; padding: 0; }
|
||||
li { font-size: 12pt; margin-bottom: 9pt; line-height: 1.35; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>OtOpcUa — the Unification Layer</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="tagline-wrap"><p class="tagline">Single sanctioned OPC UA access point per site — one session per piece of equipment.</p></div>
|
||||
<ul>
|
||||
<li><b>Two namespaces:</b> equipment (raw) + System Platform (processed) — absorbs LmxOpcUa.</li>
|
||||
<li><b>Clustered,</b> co-located on existing System Platform nodes — no new hardware footprint.</li>
|
||||
<li><b>Hybrid driver strategy:</b> proactive core library (8 drivers) + on-demand long-tail.</li>
|
||||
<li><b>OPC UA-native auth:</b> UserName tokens + standard security modes (inherited from LmxOpcUa).</li>
|
||||
<li><b>Data-path ACL model:</b> scope hierarchy + per-node permissions, committed in v2.</li>
|
||||
<li><b>Tiered cutover:</b> ScadaBridge first, Ignition second, System Platform IO last (across Years 1–3).</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
35
outputs/workspace/slides/slide11.html
Normal file
35
outputs/workspace/slides/slide11.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; }
|
||||
.tagline-wrap { border-left: 3pt solid #0066B3; padding-left: 10pt; margin: 0 0 12pt 0; }
|
||||
.tagline { font-size: 12pt; color: #2B2A80; margin: 0; font-style: italic; }
|
||||
ul { margin: 0 0 0 22pt; padding: 0; }
|
||||
li { font-size: 12pt; margin-bottom: 8pt; line-height: 1.35; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Analytics Stack — SnowBridge + Snowflake</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="tagline-wrap"><p class="tagline">SnowBridge owns the full path: ingest + in-process transform + curated-table write.</p></div>
|
||||
<ul>
|
||||
<li><b>Custom-built .NET service</b> that reads from Aveva Historian SQL (Year 1) and Redpanda/ScadaBridge (Year 2).</li>
|
||||
<li><b>In-process transforms,</b> canonical-model-aligned — aggregation, state enrichment, dim_equipment resolution.</li>
|
||||
<li><b>No dbt, no external orchestrator, no Snowflake landing tier</b> — transforms live in the service.</li>
|
||||
<li><b>Governed selection</b> of topics/tags via operator web UI with blast-radius-based approval workflow.</li>
|
||||
<li><b>≤15-minute analytics SLO</b> end-to-end (ScadaBridge emit → curated table in Snowflake).</li>
|
||||
<li><b>One "not possible before" AI/analytics use case</b> in production by end of plan — pillar 2 gate.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
35
outputs/workspace/slides/slide12.html
Normal file
35
outputs/workspace/slides/slide12.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; }
|
||||
.tagline-wrap { border-left: 3pt solid #0066B3; padding-left: 10pt; margin: 0 0 12pt 0; }
|
||||
.tagline { font-size: 12pt; color: #2B2A80; margin: 0; font-style: italic; }
|
||||
ul { margin: 0 0 0 22pt; padding: 0; }
|
||||
li { font-size: 12pt; margin-bottom: 8pt; line-height: 1.35; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Redpanda EventHub — the Async Backbone</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="tagline-wrap"><p class="tagline">Self-hosted Kafka-compatible backbone. Single-cluster HA, central in South Bend.</p></div>
|
||||
<ul>
|
||||
<li><b>Per-topic tiered retention</b> — operational 7d · analytics 30d · compliance 90d.</li>
|
||||
<li><b>Bundled Schema Registry,</b> Protobuf schemas in central <b>schemas</b> repo with <b>buf</b> CI.</li>
|
||||
<li><b>BACKWARD_TRANSITIVE compatibility</b> — consumers can upgrade independently of producers.</li>
|
||||
<li><b>Topic naming:</b> <b>{domain}.{entity}.{event-type}</b>; site identity lives in the message, not the topic.</li>
|
||||
<li><b>Auth:</b> SASL/OAUTHBEARER via enterprise IdP + prefix ACLs per producer/consumer principal.</li>
|
||||
<li><b>Store-and-forward at ScadaBridge</b> handles WAN outages; analytics-tier retention doubles as a replay surface.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
26
outputs/workspace/slides/slide13.html
Normal file
26
outputs/workspace/slides/slide13.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 14pt 30pt; flex: 1; color: #333333; }
|
||||
.sub { font-size: 11pt; color: #333333; margin: 0 0 8pt 0; font-style: italic; }
|
||||
.placeholder { width: 640pt; height: 300pt; background: transparent; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>3-Year Roadmap — Workstreams × Years</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p class="sub">Six workstreams across three years. Each cell shows the single most load-bearing commitment.</p>
|
||||
<div id="roadmap-table" class="placeholder"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
32
outputs/workspace/slides/slide14.html
Normal file
32
outputs/workspace/slides/slide14.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; }
|
||||
ul { margin: 0 0 0 22pt; padding: 0; }
|
||||
li { font-size: 12pt; margin-bottom: 9pt; line-height: 1.35; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Year 1 Focus</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<ul>
|
||||
<li><b>OtOpcUa</b> — evolve LmxOpcUa, deploy to every site, begin ScadaBridge-first cutover, UNS hierarchy walk.</li>
|
||||
<li><b>Redpanda</b> — stand up central cluster, schema registry, initial topics, publish canonical model v1.</li>
|
||||
<li><b>SnowBridge</b> — design + build; first source adapter (Historian SQL); first curated tables aligned to canonical model.</li>
|
||||
<li><b>ScadaBridge Extensions</b> — deadband / exception-based publishing + EventHub producer with store-and-forward.</li>
|
||||
<li><b>Site Onboarding</b> — no new sites; define the lightweight onboarding pattern for smaller sites.</li>
|
||||
<li><b>Legacy Retirement</b> — inventory populated (3 rows); retire first integration as pattern-proving exercise.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
44
outputs/workspace/slides/slide15.html
Normal file
44
outputs/workspace/slides/slide15.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }.body { padding: 22pt 40pt; flex: 1; color: #333333; display: flex; flex-direction: column; }
|
||||
.item { background: #ffffff; border-left: 5pt solid #0066B3; padding: 10pt 14pt; margin-bottom: 10pt; }
|
||||
.item .id { font-size: 9pt; color: #0066B3; letter-spacing: 1pt; font-weight: bold; margin: 0; }
|
||||
.item .title { font-size: 12pt; font-weight: bold; color: #2B2A80; margin: 2pt 0; }
|
||||
.item .desc { font-size: 10pt; margin: 0; color: #333333; line-height: 1.35; }
|
||||
.callout { background: #0066B3; color: #FFFFFF; padding: 8pt 14pt; border-radius: 3pt; margin-top: auto; }
|
||||
.callout p { margin: 0; font-size: 10pt; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Pillar 3 — Legacy Retirement (3 → 0)</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="item">
|
||||
<p class="id">LEG-001</p>
|
||||
<p class="title">Aveva Web API → Delmia DNC</p>
|
||||
<p class="desc">Bidirectional orchestrated handshake. Harder retirement — requires ScadaBridge scripts to re-implement System Platform parse logic.</p>
|
||||
</div>
|
||||
<div class="item">
|
||||
<p class="id">LEG-002</p>
|
||||
<p class="title">Aveva Web API ← Camstar MES</p>
|
||||
<p class="desc">Camstar-initiated. Easier retirement — ScadaBridge already has a native Camstar path; requires Camstar-side reconfiguration.</p>
|
||||
</div>
|
||||
<div class="item">
|
||||
<p class="id">LEG-003</p>
|
||||
<p class="title">System Platform → custom email notification service</p>
|
||||
<p class="desc">Easier retirement — ScadaBridge native notifications already exist.</p>
|
||||
</div>
|
||||
<div class="callout"><p><b>Carve-out:</b> Historian MSSQL reporting surface (BOBJ / Power BI) is explicitly not legacy — see legacy-integrations.md → Deliberately not tracked.</p></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
47
outputs/workspace/slides/slide16.html
Normal file
47
outputs/workspace/slides/slide16.html
Normal file
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html { background: #ffffff; }
|
||||
body {
|
||||
width: 720pt; height: 405pt; margin: 0; padding: 0;
|
||||
background: #FFFFFF; font-family: Arial, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.header { padding: 12pt 40pt 4pt 40pt; border-bottom: 1.5pt solid #0066B3; }
|
||||
.header h1 { margin: 0; font-size: 22pt; font-weight: bold; color: #0066B3; }
|
||||
.body { padding: 24pt 40pt; flex: 1; color: #333333; }
|
||||
.lede { font-size: 12pt; color: #2B2A80; margin: 0 0 14pt 0; line-height: 1.4; }
|
||||
.lede b { color: #0066B3; }
|
||||
.patterns { display: flex; gap: 18pt; margin: 0 0 12pt 0; }
|
||||
.pattern { flex: 1; background: #F5F8FB; border-top: 4pt solid #0066B3; padding: 12pt 14pt; border-radius: 3pt; }
|
||||
.pattern .tag { font-size: 8pt; color: #0066B3; letter-spacing: 1pt; margin: 0 0 4pt 0; font-weight: bold; }
|
||||
.pattern h2 { font-size: 12pt; color: #2B2A80; margin: 0 0 6pt 0; }
|
||||
.pattern p { font-size: 10pt; margin: 0; line-height: 1.4; }
|
||||
.footnote { font-size: 9pt; color: #666666; margin: 6pt 0 0 0; line-height: 1.35; font-style: italic; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Digital Twin Scope</h1>
|
||||
</div>
|
||||
<div class="body">
|
||||
<p class="lede">Scope is <b>two access-control patterns</b> — not a new component, not a new workstream. Both are delivered by architecture already committed (OtOpcUa ACL model + canonical model + single-connection-per-equipment).</p>
|
||||
|
||||
<div class="patterns">
|
||||
<div class="pattern">
|
||||
<p class="tag">PATTERN 1</p>
|
||||
<h2>Environment-lifecycle promotion</h2>
|
||||
<p>Promote between dev / staging / prod by flipping write-authority ACLs against stable equipment UUIDs — no client reconfiguration.</p>
|
||||
</div>
|
||||
<div class="pattern">
|
||||
<p class="tag">PATTERN 2</p>
|
||||
<h2>Safe read-only consumption</h2>
|
||||
<p>KPI / monitoring consumers get read-only grants with a structural zero-write-path guarantee — no equipment-side session for them to misuse.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="footnote">Out of scope for this plan: physics simulation, FAT, commissioning emulation. Those would be separate funded initiatives — adjacent to the plan, not part of it.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user