broadlistening-viewer 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # Broadlistening Ruby Viewer
2
+
3
+ [日本語版 README](./README.ja.md)
4
+
5
+ A standalone tool to visualize Broadlistening analysis JSON files.
6
+
7
+ It supports two usage modes:
8
+
9
+ 1. Static site (ruby.wasm): drag and drop JSON in the browser and view charts
10
+ 2. HTML generation (`exe/broadlistening-viewer`): generate a single self-contained HTML file from JSON on the command line
11
+
12
+ The JavaScript sources are shared with `decidim-broadlistening-view`.
13
+
14
+ ## Setup
15
+
16
+ ```bash
17
+ pnpm install
18
+ bundle install
19
+ ```
20
+
21
+ ## Static Site (ruby.wasm)
22
+
23
+ Uses ruby.wasm in the browser to load JSON files and render visualizations. The output can be deployed to static hosting platforms such as GitHub Pages or Cloudflare Pages.
24
+
25
+ ### Build
26
+
27
+ ```bash
28
+ pnpm run build:site
29
+ ```
30
+
31
+ The following files are generated in `public/`:
32
+
33
+ - `index.html` - entry point
34
+ - `broadlistening-site.js` - bundled JavaScript
35
+ - `app.css` - compiled stylesheet
36
+ - `ruby+stdlib.wasm` - ruby.wasm binary for browser runtime
37
+
38
+ ### Local Preview
39
+
40
+ ```bash
41
+ ruby -run -e httpd public
42
+ ```
43
+
44
+ Open the site in a browser, then drag and drop `hierarchical_result.json`.
45
+
46
+ ### Deploy
47
+
48
+ Deploy the `public/` directory as-is to any static hosting service.
49
+
50
+ ## HTML Generation (`exe/broadlistening-viewer`)
51
+
52
+ Generates a self-contained single HTML file from JSON. Plotly is bundled into the embedded JS, so output HTML size is larger than before.
53
+
54
+ ### Build (JS bundle)
55
+
56
+ ```bash
57
+ pnpm run build:dist
58
+ ```
59
+
60
+ ### Generate HTML
61
+
62
+ ```bash
63
+ ruby -Ilib exe/broadlistening-viewer hierarchical_result.json
64
+ # => hierarchical_result.html is generated
65
+
66
+ # Option
67
+ ruby -Ilib exe/broadlistening-viewer input.json -o output.html --title "Title"
68
+ ```
69
+
70
+ ## Build Scripts
71
+
72
+ | Command | Description |
73
+ |---------|-------------|
74
+ | `pnpm run build` | Build both site and dist outputs |
75
+ | `pnpm run build:site` | Build JS + CSS for the static site |
76
+ | `pnpm run build:dist` | Build JS + CSS bundle for `exe/broadlistening-viewer` |
77
+
78
+ ## Directory Structure
79
+
80
+ ```
81
+ ├── broadlistening-viewer.gemspec # gem specification
82
+ ├── lib/
83
+ │ └── broadlistening/
84
+ │ └── viewer/
85
+ │ ├── renderer.rb # Broadlistening::Viewer::Renderer class
86
+ │ ├── version.rb # version constant
87
+ │ └── assets/ # core shared assets (build output + templates)
88
+ │ ├── template.html.erb
89
+ │ ├── broadlistening-view.js # built by pnpm run build:dist:js
90
+ │ ├── app.css # built by pnpm run build:dist:css
91
+ │ └── i18n/
92
+ │ └── ja.json # Japanese messages
93
+ ├── exe/
94
+ │ └── broadlistening-viewer # CLI executable
95
+ ├── js/ # JS/CSS sources (shared with decidim-broadlistening-view)
96
+ │ ├── entrypoint.js
97
+ │ ├── chart_manager.js
98
+ │ ├── scatter_chart.js
99
+ │ ├── treemap_chart.js
100
+ │ ├── plotly_shim.js # standalone shim returning Plotly from npm package
101
+ │ ├── app.css
102
+ │ └── ...
103
+ ├── site/
104
+ │ └── entrypoint_site.js # entry point for static site (ruby.wasm)
105
+ ├── public/ # static site build output + index.html + ruby+stdlib.wasm
106
+ ├── build.mjs # esbuild configuration
107
+ └── package.json
108
+ ```
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "optparse"
5
+ require_relative "../lib/broadlistening/viewer/renderer"
6
+
7
+ options = { title: "分析結果", output: nil }
8
+ OptionParser.new do |opts|
9
+ opts.banner = "Usage: broadlistening-viewer INPUT_JSON [-o OUTPUT] [--title TITLE]"
10
+ opts.on("-o", "--output FILE") { |v| options[:output] = v }
11
+ opts.on("--title TITLE") { |v| options[:title] = v }
12
+ end.parse!
13
+
14
+ input_file = ARGV[0] or abort("Usage: broadlistening-viewer INPUT_JSON [-o OUTPUT] [--title TITLE]")
15
+ output_file = options[:output] || "report.html"
16
+
17
+ json_str = File.read(input_file)
18
+ renderer = Broadlistening::Viewer::Renderer.new(title: options[:title])
19
+ renderer.save(json_str, output_file)
20
+ puts "Generated: #{output_file}"
data/js/shared/blv.css ADDED
@@ -0,0 +1,102 @@
1
+ /* Plotly modebar: undo Tailwind preflight interference */
2
+ .modebar-container svg {
3
+ display: inline-block;
4
+ }
5
+
6
+ .modebar-btn {
7
+ line-height: 0;
8
+ }
9
+
10
+ /* Segment button active state (classList.toggle) */
11
+ .blv-active {
12
+ background: #fff;
13
+ color: #111827;
14
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
15
+ }
16
+
17
+ .blv-segment-btn:hover:not(.blv-active):not(:disabled) {
18
+ color: #374151;
19
+ background: rgba(255, 255, 255, 0.5);
20
+ }
21
+
22
+ /* Settings dialog (a11y-dialog-component integration) */
23
+ .blv-settings-dialog {
24
+ display: none;
25
+ position: fixed;
26
+ top: 0;
27
+ left: 0;
28
+ right: 0;
29
+ bottom: 0;
30
+ z-index: 10000;
31
+ background: rgba(0, 0, 0, 0.4);
32
+ }
33
+
34
+ .blv-settings-dialog[aria-hidden="false"] {
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ padding: 1rem;
39
+ }
40
+
41
+ .blv-settings-dialog .blv-settings-dialog__container {
42
+ display: block;
43
+ }
44
+
45
+ /* Slider range input styling */
46
+ .blv-slider input[type="range"] {
47
+ -webkit-appearance: none;
48
+ appearance: none;
49
+ width: 100%;
50
+ height: 6px;
51
+ background: #e5e7eb;
52
+ border-radius: 3px;
53
+ outline: none;
54
+ cursor: pointer;
55
+ }
56
+
57
+ .blv-slider input[type="range"]::-webkit-slider-thumb {
58
+ -webkit-appearance: none;
59
+ appearance: none;
60
+ width: 18px;
61
+ height: 18px;
62
+ background: #0284c7;
63
+ border-radius: 50%;
64
+ cursor: pointer;
65
+ transition: transform 0.15s ease;
66
+ }
67
+
68
+ .blv-slider input[type="range"]::-webkit-slider-thumb:hover {
69
+ transform: scale(1.1);
70
+ }
71
+
72
+ .blv-slider input[type="range"]::-moz-range-thumb {
73
+ width: 18px;
74
+ height: 18px;
75
+ background: #0284c7;
76
+ border: none;
77
+ border-radius: 50%;
78
+ cursor: pointer;
79
+ transition: transform 0.15s ease;
80
+ }
81
+
82
+ .blv-slider input[type="range"]::-moz-range-thumb:hover {
83
+ transform: scale(1.1);
84
+ }
85
+
86
+ .blv-slider input[type="range"]:focus::-webkit-slider-thumb {
87
+ box-shadow: 0 0 0 3px rgba(2, 132, 199, 0.3);
88
+ }
89
+
90
+ .blv-slider input[type="range"]:focus::-moz-range-thumb {
91
+ box-shadow: 0 0 0 3px rgba(2, 132, 199, 0.3);
92
+ }
93
+
94
+ /* Cluster card selection states */
95
+ .blv-cluster-card--selected {
96
+ box-shadow: 0 0 0 3px rgba(2, 132, 199, 0.4) !important;
97
+ transform: scale(1.02);
98
+ }
99
+
100
+ .blv-cluster-card--in-path {
101
+ opacity: 0.7;
102
+ }