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.
- checksums.yaml +7 -0
- data/LICENSE +661 -0
- data/README.md +108 -0
- data/exe/broadlistening-viewer +20 -0
- data/js/shared/blv.css +102 -0
- data/js/shared/chart_manager.js +504 -0
- data/js/shared/colors.js +81 -0
- data/js/shared/decidim_core_shim.js +67 -0
- data/js/shared/fullscreen_modal.js +108 -0
- data/js/shared/i18n.js +37 -0
- data/js/shared/plotly_shim.js +4034 -0
- data/js/shared/scatter_chart.js +250 -0
- data/js/shared/settings_dialog.js +154 -0
- data/js/shared/toolbar.js +102 -0
- data/js/shared/treemap_chart.js +202 -0
- data/js/shared/types.js +0 -0
- data/js/shared/utils.js +43 -0
- data/lib/broadlistening/viewer/assets/app.css +2 -0
- data/lib/broadlistening/viewer/assets/broadlistening-view.js +4201 -0
- data/lib/broadlistening/viewer/assets/i18n/ja.json +46 -0
- data/lib/broadlistening/viewer/assets/shared/_visualization_body.html.erb +33 -0
- data/lib/broadlistening/viewer/assets/template.html.erb +28 -0
- data/lib/broadlistening/viewer/renderer.rb +88 -0
- data/lib/broadlistening/viewer/version.rb +7 -0
- data/lib/broadlistening/viewer.rb +20 -0
- metadata +68 -0
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
|
+
}
|