@ggterm/core 0.3.5 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli-plot.js CHANGED
@@ -14641,6 +14641,8 @@ function handleServe(port) {
14641
14641
  }
14642
14642
  }
14643
14643
  }
14644
+ let lastNewPlotTime = 0;
14645
+ let lastBroadcastPlotId = null;
14644
14646
  const plotsDir = getPlotsDir();
14645
14647
  watch(plotsDir, (_event, filename) => {
14646
14648
  if (!filename || !filename.endsWith(".json"))
@@ -14648,12 +14650,16 @@ function handleServe(port) {
14648
14650
  if (debounceTimer)
14649
14651
  clearTimeout(debounceTimer);
14650
14652
  debounceTimer = setTimeout(() => {
14653
+ const latestId = getLatestPlotId();
14654
+ if (!latestId || latestId === lastBroadcastPlotId)
14655
+ return;
14656
+ lastBroadcastPlotId = latestId;
14657
+ lastNewPlotTime = Date.now();
14651
14658
  const payload = getLatestPayload();
14652
14659
  if (payload)
14653
14660
  broadcast(payload);
14654
14661
  }, 150);
14655
14662
  });
14656
- const vegaLitePath = join3(getGGTermDir(), "last-plot-vegalite.json");
14657
14663
  let styleDebounce = null;
14658
14664
  watch(getGGTermDir(), (_event, filename) => {
14659
14665
  if (filename !== "last-plot-vegalite.json")
@@ -14661,15 +14667,23 @@ function handleServe(port) {
14661
14667
  if (styleDebounce)
14662
14668
  clearTimeout(styleDebounce);
14663
14669
  styleDebounce = setTimeout(() => {
14670
+ if (Date.now() - lastNewPlotTime < 2000)
14671
+ return;
14672
+ const vegaLitePath = join3(getGGTermDir(), "last-plot-vegalite.json");
14664
14673
  if (!existsSync3(vegaLitePath))
14665
14674
  return;
14666
14675
  try {
14667
14676
  const spec = JSON.parse(readFileSync3(vegaLitePath, "utf-8"));
14668
14677
  const latestId = getLatestPlotId();
14669
- const provenance = latestId ? { id: latestId, description: "Styled plot", timestamp: new Date().toISOString(), geomTypes: [] } : { id: "styled", description: "Styled plot", timestamp: new Date().toISOString(), geomTypes: [] };
14670
- broadcast(JSON.stringify({ type: "plot", spec, provenance }));
14678
+ const provenance = {
14679
+ id: latestId || "styled",
14680
+ description: "Styled plot",
14681
+ timestamp: new Date().toISOString(),
14682
+ geomTypes: []
14683
+ };
14684
+ broadcast(JSON.stringify({ type: "update", spec, provenance }));
14671
14685
  } catch {}
14672
- }, 200);
14686
+ }, 300);
14673
14687
  });
14674
14688
  const server = createServer((req, res) => {
14675
14689
  const url = new URL(req.url || "/", `http://localhost:${p}`);
@@ -15080,6 +15094,14 @@ async function renderSpec(spec) {
15080
15094
  }
15081
15095
  }
15082
15096
 
15097
+ // Re-render on container resize so plot reflows correctly
15098
+ let resizeTimer = null;
15099
+ new ResizeObserver(() => {
15100
+ if (!view) return;
15101
+ if (resizeTimer) clearTimeout(resizeTimer);
15102
+ resizeTimer = setTimeout(() => { view.resize(); }, 150);
15103
+ }).observe(vis);
15104
+
15083
15105
  async function showPlot(data) {
15084
15106
  await renderSpec(data.spec);
15085
15107
  updateMeta(data.provenance);
@@ -15181,6 +15203,12 @@ function connect() {
15181
15203
  showPlot(data);
15182
15204
  updateNav();
15183
15205
  updateHistoryHighlight();
15206
+ } else if (data.type === 'update') {
15207
+ // Style/customize change — replace current plot, don't add to history
15208
+ if (currentIdx >= 0 && currentIdx < history.length) {
15209
+ history[currentIdx] = data;
15210
+ }
15211
+ showPlot(data);
15184
15212
  }
15185
15213
  };
15186
15214
  }
@@ -15212,7 +15240,7 @@ import { join as join4 } from "path";
15212
15240
  // src/init.ts
15213
15241
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync2, existsSync as existsSync2, readdirSync as readdirSync2, statSync } from "fs";
15214
15242
  import { join as join2, extname } from "path";
15215
- var SKILLS_VERSION = "0.3.4";
15243
+ var SKILLS_VERSION = "0.3.7";
15216
15244
  var SKILLS = {
15217
15245
  "data-load": {
15218
15246
  files: {
@@ -15489,30 +15517,41 @@ allowed-tools: Read, Write, Bash(npx:*)
15489
15517
 
15490
15518
  Modify Vega-Lite specifications based on natural language requests.
15491
15519
 
15492
- ## Files
15520
+ ## CRITICAL: Which File to Edit
15521
+
15522
+ **ALWAYS read and write \`.ggterm/last-plot-vegalite.json\`** — this is the Vega-Lite spec that the viewer renders.
15493
15523
 
15494
- After creating a plot, these files exist:
15495
- - \`.ggterm/last-plot.json\` - Original PlotSpec
15496
- - \`.ggterm/last-plot-vegalite.json\` - Vega-Lite spec to modify
15524
+ **NEVER modify \`.ggterm/last-plot.json\`** that is the ggterm terminal format. Changes to it will NOT appear in the viewer.
15497
15525
 
15498
15526
  ## Common Customizations
15499
15527
 
15528
+ All examples below modify properties of the Vega-Lite spec read from \`.ggterm/last-plot-vegalite.json\`:
15529
+
15500
15530
  ### Title and Labels
15501
15531
 
15502
15532
  \`\`\`javascript
15503
15533
  spec.title = "New Title"
15534
+ spec.title = { text: "Title", subtitle: "Subtitle" }
15504
15535
  spec.encoding.x.title = "X Axis Label"
15505
15536
  spec.encoding.y.title = "Y Axis Label"
15537
+ spec.encoding.color.title = "Legend Title"
15506
15538
  \`\`\`
15507
15539
 
15508
15540
  ### Colors
15509
15541
 
15510
15542
  \`\`\`javascript
15511
- // Color scheme for categorical data
15512
- spec.encoding.color.scale = { scheme: "category10" }
15543
+ spec.encoding.color.scale = { scheme: "category10" } // categorical
15544
+ spec.encoding.color.scale = { scheme: "viridis" } // continuous
15545
+ spec.mark.color = "#3366cc" // single color
15546
+ \`\`\`
15513
15547
 
15514
- // Color scheme for continuous data
15515
- spec.encoding.color.scale = { scheme: "viridis" }
15548
+ ### Fonts and Config
15549
+
15550
+ \`\`\`javascript
15551
+ spec.config = spec.config || {}
15552
+ spec.config.font = "Helvetica"
15553
+ spec.config.title = { fontSize: 18, fontWeight: "bold" }
15554
+ spec.config.axis = { labelFontSize: 12, titleFontSize: 14 }
15516
15555
  \`\`\`
15517
15556
 
15518
15557
  ### Dimensions
@@ -15524,9 +15563,9 @@ spec.height = 400
15524
15563
 
15525
15564
  ## Workflow
15526
15565
 
15527
- 1. Read \`.ggterm/last-plot-vegalite.json\`
15528
- 2. Apply the requested modifications
15529
- 3. Write the updated spec back to \`.ggterm/last-plot-vegalite.json\`
15566
+ 1. Read \`.ggterm/last-plot-vegalite.json\` (NOT last-plot.json)
15567
+ 2. Parse as JSON, apply the requested modifications
15568
+ 3. Write the updated JSON back to \`.ggterm/last-plot-vegalite.json\`
15530
15569
  4. **DONE** — the live viewer auto-detects the change and displays the customized plot
15531
15570
 
15532
15571
  **IMPORTANT**: Do NOT re-create the plot with \`npx ggterm-plot\` after customizing. The viewer watches the spec file and auto-updates. Re-running the plot command would overwrite your customizations.
@@ -15547,26 +15586,107 @@ allowed-tools: Read, Write, Bash(npx:*)
15547
15586
 
15548
15587
  Apply expert-curated style presets to Vega-Lite specifications for publication-quality output.
15549
15588
 
15550
- ## Available Presets
15589
+ ## CRITICAL: Which File to Edit
15590
+
15591
+ **ALWAYS read and write \`.ggterm/last-plot-vegalite.json\`** — this is the Vega-Lite spec that the viewer renders.
15551
15592
 
15552
- | Preset | Inspiration | Key Characteristics |
15553
- |--------|-------------|---------------------|
15554
- | \`wilke\` | Claus Wilke's *Fundamentals of Data Visualization* | Minimal, clean, no chartjunk, subtle gridlines |
15555
- | \`tufte\` | Edward Tufte's data-ink ratio principles | Maximum data-ink, no grid, no borders |
15556
- | \`nature\` | Nature journal style | Clean, serif fonts, specific dimensions |
15557
- | \`economist\` | The Economist charts | Bold colors, distinctive style |
15558
- | \`minimal\` | Generic minimal style | No grid, no borders, clean |
15559
- | \`apa\` | APA publication guidelines | Academic papers, grayscale-friendly |
15593
+ **NEVER modify \`.ggterm/last-plot.json\`** that is the ggterm terminal format. Changes to it will NOT appear in the viewer.
15560
15594
 
15561
15595
  ## Workflow
15562
15596
 
15563
- 1. Read \`.ggterm/last-plot-vegalite.json\`
15564
- 2. Apply the requested style preset (only modify \`config\` — do NOT change \`encoding\`, \`data\`, or \`mark\` structure)
15565
- 3. Write the updated spec back to \`.ggterm/last-plot-vegalite.json\`
15566
- 4. **DONE** the live viewer auto-detects the change and displays the styled plot
15567
- 5. Inform user they can export with \`/ggterm-publish\`
15597
+ 1. Read \`.ggterm/last-plot-vegalite.json\` (NOT last-plot.json)
15598
+ 2. Parse as JSON
15599
+ 3. Set \`spec.config\` to the style config below do NOT change \`encoding\`, \`data\`, or \`mark\`
15600
+ 4. Write the updated JSON back to \`.ggterm/last-plot-vegalite.json\`
15601
+ 5. **DONE** the live viewer auto-detects the change and displays the styled plot
15602
+
15603
+ **IMPORTANT**: Do NOT re-create the plot with \`npx ggterm-plot\` after styling. Re-running would overwrite your style changes.
15604
+
15605
+ ## Style Configs (Vega-Lite \`config\` property)
15606
+
15607
+ ### Wilke (default for "publication ready")
15608
+
15609
+ \`\`\`json
15610
+ {
15611
+ "font": "Helvetica Neue, Helvetica, Arial, sans-serif",
15612
+ "background": "white",
15613
+ "view": { "stroke": null },
15614
+ "title": { "fontSize": 14, "fontWeight": "normal", "anchor": "start", "offset": 12 },
15615
+ "axis": { "domain": true, "domainColor": "#333333", "domainWidth": 1, "grid": false, "labelColor": "#333333", "labelFontSize": 11, "tickColor": "#333333", "tickSize": 5, "titleColor": "#333333", "titleFontSize": 12, "titleFontWeight": "normal", "titlePadding": 10 },
15616
+ "axisY": { "grid": true, "gridColor": "#ebebeb", "gridWidth": 0.5 },
15617
+ "legend": { "labelFontSize": 11, "titleFontSize": 11, "titleFontWeight": "normal", "symbolSize": 100 },
15618
+ "range": { "category": ["#4C78A8", "#F58518", "#E45756", "#72B7B2", "#54A24B", "#EECA3B", "#B279A2", "#FF9DA6"] }
15619
+ }
15620
+ \`\`\`
15621
+
15622
+ ### Tufte
15623
+
15624
+ \`\`\`json
15625
+ {
15626
+ "font": "Georgia, serif",
15627
+ "background": "white",
15628
+ "view": { "stroke": null },
15629
+ "title": { "fontSize": 13, "fontWeight": "normal", "anchor": "start" },
15630
+ "axis": { "domain": false, "grid": false, "labelColor": "#333333", "labelFontSize": 10, "ticks": false, "titleColor": "#333333", "titleFontSize": 11, "titleFontWeight": "normal" },
15631
+ "legend": { "labelFontSize": 10, "titleFontSize": 10, "titleFontWeight": "normal" },
15632
+ "range": { "category": ["#333333", "#666666", "#999999", "#CCCCCC"] }
15633
+ }
15634
+ \`\`\`
15635
+
15636
+ ### Nature
15568
15637
 
15569
- **IMPORTANT**: Do NOT re-create the plot with \`npx ggterm-plot\` after styling. The viewer watches the spec file and auto-updates. Re-running the plot command would overwrite your style changes.
15638
+ \`\`\`json
15639
+ {
15640
+ "font": "Arial, Helvetica, sans-serif",
15641
+ "background": "white",
15642
+ "view": { "stroke": null },
15643
+ "title": { "fontSize": 10, "fontWeight": "bold" },
15644
+ "axis": { "domain": true, "domainColor": "#000000", "domainWidth": 0.5, "grid": false, "labelColor": "#000000", "labelFontSize": 8, "tickColor": "#000000", "tickSize": 4, "tickWidth": 0.5, "titleColor": "#000000", "titleFontSize": 9, "titleFontWeight": "normal" },
15645
+ "legend": { "labelFontSize": 8, "titleFontSize": 8, "symbolSize": 50 }
15646
+ }
15647
+ \`\`\`
15648
+
15649
+ ### Economist
15650
+
15651
+ \`\`\`json
15652
+ {
15653
+ "font": "Officina Sans, Arial, sans-serif",
15654
+ "background": "#d5e4eb",
15655
+ "view": { "stroke": null },
15656
+ "title": { "fontSize": 14, "fontWeight": "bold", "color": "#000000", "anchor": "start" },
15657
+ "axis": { "domain": false, "grid": true, "gridColor": "#ffffff", "gridWidth": 1, "labelColor": "#000000", "labelFontSize": 11, "titleFontSize": 12, "titleFontWeight": "bold" },
15658
+ "axisX": { "grid": false, "domain": true, "domainColor": "#000000" },
15659
+ "legend": { "orient": "top", "labelFontSize": 11, "titleFontSize": 11 },
15660
+ "range": { "category": ["#006BA2", "#3EBCD2", "#379A8B", "#EBB434", "#B4BA39", "#9A607F", "#D73F3F"] }
15661
+ }
15662
+ \`\`\`
15663
+
15664
+ ### Minimal
15665
+
15666
+ \`\`\`json
15667
+ {
15668
+ "font": "system-ui, -apple-system, sans-serif",
15669
+ "background": "white",
15670
+ "view": { "stroke": null },
15671
+ "title": { "fontSize": 14, "fontWeight": "normal" },
15672
+ "axis": { "domain": false, "grid": false, "ticks": false, "labelColor": "#666666", "labelFontSize": 11, "titleColor": "#333333", "titleFontSize": 12, "titleFontWeight": "normal" },
15673
+ "legend": { "labelFontSize": 11, "titleFontSize": 11, "titleFontWeight": "normal" }
15674
+ }
15675
+ \`\`\`
15676
+
15677
+ ### APA
15678
+
15679
+ \`\`\`json
15680
+ {
15681
+ "font": "Times New Roman, serif",
15682
+ "background": "white",
15683
+ "view": { "stroke": null },
15684
+ "title": { "fontSize": 12, "fontWeight": "bold", "anchor": "middle" },
15685
+ "axis": { "domain": true, "domainColor": "#000000", "grid": false, "labelColor": "#000000", "labelFontSize": 10, "tickColor": "#000000", "titleColor": "#000000", "titleFontSize": 11, "titleFontWeight": "normal", "titleFontStyle": "italic" },
15686
+ "legend": { "labelFontSize": 10, "titleFontSize": 10, "titleFontStyle": "italic" },
15687
+ "range": { "category": ["#000000", "#666666", "#999999", "#CCCCCC"] }
15688
+ }
15689
+ \`\`\`
15570
15690
 
15571
15691
  ## Response Format
15572
15692
 
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAsgBH,wBAAgB,UAAU,IAAI,IAAI,CA2JjC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkmBH,wBAAgB,UAAU,IAAI,IAAI,CA2JjC"}
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA0eH,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAkI/C"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAwfH,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAqJ/C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggterm/core",
3
- "version": "0.3.5",
3
+ "version": "0.3.8",
4
4
  "description": "Grammar of Graphics engine for terminals",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",