xlsxrb 0.1.11 → 0.1.13
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 +4 -4
- data/CHANGELOG.md +86 -34
- data/README.md +26 -21
- data/lib/xlsxrb/chart_builder.rb +18 -18
- data/lib/xlsxrb/dsl_helpers.rb +175 -0
- data/lib/xlsxrb/elements/cell.rb +56 -22
- data/lib/xlsxrb/elements/column.rb +27 -3
- data/lib/xlsxrb/elements/row.rb +27 -8
- data/lib/xlsxrb/elements/workbook.rb +27 -14
- data/lib/xlsxrb/elements/worksheet.rb +17 -1
- data/lib/xlsxrb/ooxml/cfb.rb +9 -6
- data/lib/xlsxrb/ooxml/crypto/agile.rb +5 -5
- data/lib/xlsxrb/ooxml/reader/listeners/core_listeners.rb +784 -0
- data/lib/xlsxrb/ooxml/reader/listeners/drawing_listeners.rb +3478 -0
- data/lib/xlsxrb/ooxml/reader/listeners/feature_listeners.rb +1796 -0
- data/lib/xlsxrb/ooxml/reader/listeners/style_listeners.rb +428 -0
- data/lib/xlsxrb/ooxml/reader/listeners.rb +4 -6446
- data/lib/xlsxrb/ooxml/shared_strings_parser.rb +3 -5
- data/lib/xlsxrb/ooxml/utils.rb +5 -6
- data/lib/xlsxrb/ooxml/workbook_writer.rb +5 -7
- data/lib/xlsxrb/ooxml/worksheet_parser.rb +172 -9
- data/lib/xlsxrb/ooxml/writer/drawing_xml.rb +1602 -0
- data/lib/xlsxrb/ooxml/writer/features_xml.rb +731 -0
- data/lib/xlsxrb/ooxml/writer/styles_xml.rb +348 -0
- data/lib/xlsxrb/ooxml/writer.rb +9 -2646
- data/lib/xlsxrb/ooxml/xml_builder.rb +31 -2
- data/lib/xlsxrb/ooxml/zip_generator.rb +26 -5
- data/lib/xlsxrb/ooxml/zip_reader.rb +364 -78
- data/lib/xlsxrb/ooxml/zip_writer.rb +32 -5
- data/lib/xlsxrb/stream_sheet.rb +94 -6
- data/lib/xlsxrb/stream_writer.rb +48 -115
- data/lib/xlsxrb/style_builder.rb +10 -1
- data/lib/xlsxrb/version.rb +1 -1
- data/lib/xlsxrb/workbook_builder.rb +1 -1
- data/lib/xlsxrb/worksheet_builder.rb +19 -81
- data/lib/xlsxrb.rb +449 -171
- data/sig/generated/xlsxrb/chart_builder.rbs +36 -36
- data/sig/generated/xlsxrb/dsl_helpers.rbs +92 -0
- data/sig/generated/xlsxrb/elements/cell.rbs +29 -0
- data/sig/generated/xlsxrb/elements/column.rbs +32 -21
- data/sig/generated/xlsxrb/elements/row.rbs +94 -14
- data/sig/generated/xlsxrb/elements/workbook.rbs +102 -10
- data/sig/generated/xlsxrb/elements/worksheet.rbs +9 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners/core_listeners.rbs +173 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners/drawing_listeners.rbs +400 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners/feature_listeners.rbs +442 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners/style_listeners.rbs +80 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners.rbs +0 -1072
- data/sig/generated/xlsxrb/ooxml/shared_strings_parser.rbs +0 -2
- data/sig/generated/xlsxrb/ooxml/utils.rbs +2 -0
- data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +5 -4
- data/sig/generated/xlsxrb/ooxml/writer/drawing_xml.rbs +79 -0
- data/sig/generated/xlsxrb/ooxml/writer/features_xml.rbs +83 -0
- data/sig/generated/xlsxrb/ooxml/writer/styles_xml.rbs +53 -0
- data/sig/generated/xlsxrb/ooxml/writer.rbs +6 -185
- data/sig/generated/xlsxrb/ooxml/xml_builder.rbs +18 -0
- data/sig/generated/xlsxrb/ooxml/zip_generator.rbs +15 -0
- data/sig/generated/xlsxrb/ooxml/zip_reader.rbs +96 -8
- data/sig/generated/xlsxrb/ooxml/zip_writer.rbs +5 -1
- data/sig/generated/xlsxrb/stream_sheet.rbs +43 -3
- data/sig/generated/xlsxrb/style_builder.rbs +7 -0
- data/sig/generated/xlsxrb.rbs +18 -11
- metadata +22 -144
- data/.devcontainer/Dockerfile +0 -65
- data/.devcontainer/devcontainer.json +0 -17
- data/.gem_rbs_collection/ast/2.4/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/ast/2.4/ast.rbs +0 -73
- data/.gem_rbs_collection/concurrent-ruby/1.1/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/concurrent-ruby/1.1/array.rbs +0 -4
- data/.gem_rbs_collection/concurrent-ruby/1.1/atomic_reference.rbs +0 -16
- data/.gem_rbs_collection/concurrent-ruby/1.1/executor.rbs +0 -96
- data/.gem_rbs_collection/concurrent-ruby/1.1/hash.rbs +0 -4
- data/.gem_rbs_collection/concurrent-ruby/1.1/map.rbs +0 -68
- data/.gem_rbs_collection/concurrent-ruby/1.1/promises.rbs +0 -249
- data/.gem_rbs_collection/concurrent-ruby/1.1/set.rbs +0 -4
- data/.gem_rbs_collection/concurrent-ruby/1.1/timer_task.rbs +0 -47
- data/.gem_rbs_collection/concurrent-ruby/1.1/utility/processor_counter.rbs +0 -5
- data/.gem_rbs_collection/csv/3.3/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/csv/3.3/csv.rbs +0 -3871
- data/.gem_rbs_collection/csv/3.3/manifest.yaml +0 -3
- data/.gem_rbs_collection/lint_roller/1.1/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/lint_roller/1.1/lint_roller.rbs +0 -48
- data/.gem_rbs_collection/listen/3.9/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/listen/3.9/listen.rbs +0 -25
- data/.gem_rbs_collection/listen/3.9/listener.rbs +0 -24
- data/.gem_rbs_collection/logger/1.7/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/logger/1.7/formatter.rbs +0 -45
- data/.gem_rbs_collection/logger/1.7/log_device.rbs +0 -100
- data/.gem_rbs_collection/logger/1.7/logger.rbs +0 -796
- data/.gem_rbs_collection/logger/1.7/manifest.yaml +0 -2
- data/.gem_rbs_collection/logger/1.7/period.rbs +0 -17
- data/.gem_rbs_collection/logger/1.7/severity.rbs +0 -34
- data/.gem_rbs_collection/nokogiri/1.11/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/nokogiri/1.11/nokogiri.rbs +0 -2332
- data/.gem_rbs_collection/nokogiri/1.11/patch.rbs +0 -4
- data/.gem_rbs_collection/parallel/1.20/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/parallel/1.20/parallel.rbs +0 -86
- data/.gem_rbs_collection/parser/3.2/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/parser/3.2/manifest.yaml +0 -7
- data/.gem_rbs_collection/parser/3.2/parser.rbs +0 -194
- data/.gem_rbs_collection/parser/3.2/polyfill.rbs +0 -4
- data/.gem_rbs_collection/rainbow/3.0/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rainbow/3.0/global.rbs +0 -7
- data/.gem_rbs_collection/rainbow/3.0/presenter.rbs +0 -209
- data/.gem_rbs_collection/rainbow/3.0/rainbow.rbs +0 -5
- data/.gem_rbs_collection/rake/13.0/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rake/13.0/manifest.yaml +0 -2
- data/.gem_rbs_collection/rake/13.0/rake.rbs +0 -39
- data/.gem_rbs_collection/regexp_parser/2.8/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/regexp_parser/2.8/regexp_parser.rbs +0 -17
- data/.gem_rbs_collection/rubocop/1.57/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rubocop/1.57/rubocop.rbs +0 -208
- data/.gem_rbs_collection/rubocop-ast/1.46/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rubocop-ast/1.46/rubocop-ast.rbs +0 -903
- data/.gem_rbs_collection/rubyzip/3.2/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rubyzip/3.2/manifest.yaml +0 -8
- data/.gem_rbs_collection/rubyzip/3.2/zip/central_directory.rbs +0 -42
- data/.gem_rbs_collection/rubyzip/3.2/zip/compressor.rbs +0 -5
- data/.gem_rbs_collection/rubyzip/3.2/zip/constants.rbs +0 -47
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/aes_encryption.rbs +0 -30
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/decrypted_io.rbs +0 -9
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/encryption.rbs +0 -7
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/null_encryption.rbs +0 -19
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/traditional_encryption.rbs +0 -31
- data/.gem_rbs_collection/rubyzip/3.2/zip/decompressor.rbs +0 -18
- data/.gem_rbs_collection/rubyzip/3.2/zip/deflater.rbs +0 -12
- data/.gem_rbs_collection/rubyzip/3.2/zip/dirtyable.rbs +0 -11
- data/.gem_rbs_collection/rubyzip/3.2/zip/dos_time.rbs +0 -13
- data/.gem_rbs_collection/rubyzip/3.2/zip/entry.rbs +0 -95
- data/.gem_rbs_collection/rubyzip/3.2/zip/entry_set.rbs +0 -31
- data/.gem_rbs_collection/rubyzip/3.2/zip/errors.rbs +0 -58
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/aes.rbs +0 -24
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/generic.rbs +0 -17
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/ntfs.rbs +0 -23
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/old_unix.rbs +0 -22
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/universal_time.rbs +0 -30
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unix.rbs +0 -20
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unknown.rbs +0 -15
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/zip64.rbs +0 -26
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field.rbs +0 -21
- data/.gem_rbs_collection/rubyzip/3.2/zip/file.rbs +0 -131
- data/.gem_rbs_collection/rubyzip/3.2/zip/file_split.rbs +0 -14
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/dir.rbs +0 -33
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/directory_iterator.rbs +0 -21
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file.rbs +0 -63
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file_stat.rbs +0 -55
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/zip_file_name_mapper.rbs +0 -35
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem.rbs +0 -7
- data/.gem_rbs_collection/rubyzip/3.2/zip/inflater.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/input_stream.rbs +0 -22
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_input_stream.rbs +0 -29
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_output_stream.rbs +0 -17
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras.rbs +0 -13
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_compressor.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_decompressor.rbs +0 -8
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_input_stream.rbs +0 -6
- data/.gem_rbs_collection/rubyzip/3.2/zip/output_stream.rbs +0 -30
- data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_compressor.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_decompressor.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_directory.rbs +0 -5
- data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_stream.rbs +0 -15
- data/.gem_rbs_collection/rubyzip/3.2/zip/version.rbs +0 -3
- data/.gem_rbs_collection/rubyzip/3.2/zip.rbs +0 -40
- data/Rakefile +0 -567
- data/Steepfile +0 -18
- data/benchmark.rb +0 -406
- data/docs/ARCHITECTURE.md +0 -514
- data/docs/DEVELOPMENT.md +0 -133
- data/docs/PEER_LIBRARIES.md +0 -121
- data/docs/QUALITY_ASSURANCE.md +0 -25
- data/docs/SPEC_SOURCES.md +0 -48
- data/docs/assets/benchmark_results.svg +0 -144
- data/docs/assets/lsp_autocompletion.png +0 -0
- data/docs/assets/playground_preview.png +0 -0
- data/docs/coi-serviceworker.js +0 -82
- data/docs/office_thread.js +0 -77
- data/docs/preview.html +0 -719
- data/docs/visual/VisualGallery.md +0 -4672
- data/docs/wasm/ruby.wasm +0 -0
- data/docs/wasm/wasm_doc_helper.css +0 -351
- data/docs/wasm/wasm_doc_helper.js +0 -439
- data/docs/zeta.js +0 -1107
- data/rbs_collection.lock.yaml +0 -252
- data/rbs_collection.yaml +0 -19
- data/vendor/sdk_runner/Program.cs +0 -93
- data/vendor/sdk_runner/sdk_runner.csproj +0 -13
|
@@ -1,439 +0,0 @@
|
|
|
1
|
-
// wasm_doc_helper.js
|
|
2
|
-
(function () {
|
|
3
|
-
// Detect all pre.ruby blocks to setup interactive playgrounds
|
|
4
|
-
document.addEventListener("DOMContentLoaded", () => {
|
|
5
|
-
const codeBlocks = document.querySelectorAll("pre.ruby");
|
|
6
|
-
codeBlocks.forEach((block, index) => {
|
|
7
|
-
setupLazyPlayground(block, index);
|
|
8
|
-
});
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
let rubyVM = null;
|
|
12
|
-
let isInitializing = false;
|
|
13
|
-
const initListeners = [];
|
|
14
|
-
let consoleBuffer = ""; // Temporary buffer to capture stdout/stderr
|
|
15
|
-
|
|
16
|
-
// Helper to initialize and retrieve the Ruby Wasm VM
|
|
17
|
-
async function ensureRubyWasm() {
|
|
18
|
-
if (rubyVM) return rubyVM;
|
|
19
|
-
if (isInitializing) {
|
|
20
|
-
return new Promise((resolve) => initListeners.push(resolve));
|
|
21
|
-
}
|
|
22
|
-
isInitializing = true;
|
|
23
|
-
|
|
24
|
-
// Load browser.umd.js locally using the relative path prefix
|
|
25
|
-
const prefix = window.rdoc_rel_prefix || "./";
|
|
26
|
-
await loadScript(`${prefix}wasm/browser.umd.js`);
|
|
27
|
-
|
|
28
|
-
// Fetch and compile the custom prepackaged ruby.wasm module
|
|
29
|
-
const response = await fetch(`${prefix}wasm/ruby.wasm?cb=${Date.now()}`);
|
|
30
|
-
const buffer = await response.arrayBuffer();
|
|
31
|
-
const module = await WebAssembly.compile(buffer);
|
|
32
|
-
|
|
33
|
-
// Capture stdout/stderr streams to buffer during execution
|
|
34
|
-
const rubyWasm = window.rubyWasm || window["ruby-wasm-wasi"];
|
|
35
|
-
const DefaultRubyVM = rubyWasm.DefaultRubyVM;
|
|
36
|
-
let vmInit;
|
|
37
|
-
|
|
38
|
-
const options = {
|
|
39
|
-
stdout: (str) => { consoleBuffer += str; },
|
|
40
|
-
stderr: (str) => { consoleBuffer += str; }
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
// Handle initialization polymorphism across various ruby-wasm versions
|
|
44
|
-
if (typeof DefaultRubyVM.initialize === "function") {
|
|
45
|
-
vmInit = DefaultRubyVM.initialize(module, options);
|
|
46
|
-
} else if (typeof DefaultRubyVM.initializeVM === "function") {
|
|
47
|
-
vmInit = DefaultRubyVM.initializeVM(module, options);
|
|
48
|
-
} else if (typeof DefaultRubyVM === "function") {
|
|
49
|
-
try {
|
|
50
|
-
vmInit = DefaultRubyVM(module, options);
|
|
51
|
-
} catch (e) {
|
|
52
|
-
const instance = new DefaultRubyVM();
|
|
53
|
-
const initFn = instance.initialize || instance.initializeVM;
|
|
54
|
-
if (typeof initFn === "function") {
|
|
55
|
-
vmInit = initFn.call(instance, module, options);
|
|
56
|
-
} else {
|
|
57
|
-
throw new Error("No initialization method found on DefaultRubyVM instance");
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
throw new Error("DefaultRubyVM is not available");
|
|
62
|
-
}
|
|
63
|
-
const { vm } = await vmInit;
|
|
64
|
-
|
|
65
|
-
rubyVM = vm;
|
|
66
|
-
|
|
67
|
-
isInitializing = false;
|
|
68
|
-
const listeners = [...initListeners];
|
|
69
|
-
initListeners.length = 0;
|
|
70
|
-
listeners.forEach((resolve) => resolve(rubyVM));
|
|
71
|
-
return rubyVM;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Inject script tags dynamically
|
|
75
|
-
function loadScript(src) {
|
|
76
|
-
return new Promise((resolve, reject) => {
|
|
77
|
-
if (document.querySelector(`script[src="${src}"]`)) {
|
|
78
|
-
resolve();
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
const script = document.createElement("script");
|
|
82
|
-
script.src = src;
|
|
83
|
-
script.onload = resolve;
|
|
84
|
-
script.onerror = reject;
|
|
85
|
-
document.head.appendChild(script);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Setup lightweight wrapper and action buttons, deferring full editor instantiation until hover or click
|
|
90
|
-
function setupLazyPlayground(block, index) {
|
|
91
|
-
const originalCode = block.textContent;
|
|
92
|
-
|
|
93
|
-
// Create wrapper container
|
|
94
|
-
const wrapper = document.createElement("div");
|
|
95
|
-
wrapper.className = "wasm-playground-wrapper";
|
|
96
|
-
block.parentNode.insertBefore(wrapper, block);
|
|
97
|
-
wrapper.appendChild(block);
|
|
98
|
-
|
|
99
|
-
// Create action button bar (always visible)
|
|
100
|
-
const btnBar = document.createElement("div");
|
|
101
|
-
btnBar.className = "wasm-quick-action-bar";
|
|
102
|
-
|
|
103
|
-
const previewBtn = document.createElement("button");
|
|
104
|
-
previewBtn.className = "wasm-quick-btn wasm-quick-preview-btn";
|
|
105
|
-
previewBtn.innerHTML = `
|
|
106
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px; vertical-align: middle;">
|
|
107
|
-
<polygon points="5 3 19 12 5 21 5 3"></polygon>
|
|
108
|
-
</svg>Live Preview
|
|
109
|
-
`;
|
|
110
|
-
|
|
111
|
-
const downloadBtn = document.createElement("button");
|
|
112
|
-
downloadBtn.className = "wasm-quick-btn wasm-quick-download-btn";
|
|
113
|
-
downloadBtn.innerHTML = `
|
|
114
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px; vertical-align: middle;">
|
|
115
|
-
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
|
116
|
-
<polyline points="7 10 12 15 17 10"></polyline>
|
|
117
|
-
<line x1="12" y1="15" x2="12" y2="3"></line>
|
|
118
|
-
</svg>Download XLSX
|
|
119
|
-
`;
|
|
120
|
-
|
|
121
|
-
const resetBtn = document.createElement("button");
|
|
122
|
-
resetBtn.className = "wasm-quick-btn wasm-quick-reset-btn";
|
|
123
|
-
resetBtn.innerHTML = `
|
|
124
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px; vertical-align: middle;">
|
|
125
|
-
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"></path>
|
|
126
|
-
<polyline points="3 3 3 8 8 8"></polyline>
|
|
127
|
-
</svg>Reset
|
|
128
|
-
`;
|
|
129
|
-
|
|
130
|
-
btnBar.appendChild(previewBtn);
|
|
131
|
-
btnBar.appendChild(downloadBtn);
|
|
132
|
-
btnBar.appendChild(resetBtn);
|
|
133
|
-
|
|
134
|
-
// Insert button bar right below the wrapper in the DOM
|
|
135
|
-
wrapper.parentNode.insertBefore(btnBar, wrapper.nextSibling);
|
|
136
|
-
|
|
137
|
-
let isEditorInitialized = false;
|
|
138
|
-
let textarea = null;
|
|
139
|
-
|
|
140
|
-
// Full initialization of the transparent overlay editor (Deferred)
|
|
141
|
-
function initializeEditorOnDemand() {
|
|
142
|
-
if (isEditorInitialized) return;
|
|
143
|
-
isEditorInitialized = true;
|
|
144
|
-
|
|
145
|
-
// Create transparent textarea overlay
|
|
146
|
-
textarea = document.createElement("textarea");
|
|
147
|
-
textarea.className = "wasm-inline-editor";
|
|
148
|
-
textarea.value = originalCode;
|
|
149
|
-
textarea.spellcheck = false;
|
|
150
|
-
wrapper.insertBefore(textarea, block);
|
|
151
|
-
|
|
152
|
-
// Copy exact layout styles from pre block to align overlay perfectly
|
|
153
|
-
const preStyle = window.getComputedStyle(block);
|
|
154
|
-
const stylesToCopy = [
|
|
155
|
-
"fontFamily", "fontSize", "lineHeight", "fontWeight",
|
|
156
|
-
"paddingTop", "paddingBottom", "paddingLeft", "paddingRight",
|
|
157
|
-
"marginTop", "marginBottom", "marginLeft", "marginRight",
|
|
158
|
-
"textAlign"
|
|
159
|
-
];
|
|
160
|
-
stylesToCopy.forEach(prop => {
|
|
161
|
-
textarea.style[prop] = preStyle[prop];
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
// Enforce overlapping styles
|
|
165
|
-
block.style.position = "relative";
|
|
166
|
-
block.style.pointerEvents = "none"; // Clicks pass to textarea
|
|
167
|
-
block.style.whiteSpace = "pre-wrap";
|
|
168
|
-
block.style.wordBreak = "break-all";
|
|
169
|
-
block.style.zIndex = "1";
|
|
170
|
-
block.style.margin = "0";
|
|
171
|
-
|
|
172
|
-
textarea.style.position = "absolute";
|
|
173
|
-
textarea.style.top = "0";
|
|
174
|
-
textarea.style.left = "0";
|
|
175
|
-
textarea.style.width = "100%";
|
|
176
|
-
textarea.style.height = "100%";
|
|
177
|
-
textarea.style.background = "transparent";
|
|
178
|
-
textarea.style.color = "transparent";
|
|
179
|
-
textarea.style.caretColor = "#2563eb"; // Sleek blue cursor
|
|
180
|
-
textarea.style.border = "none";
|
|
181
|
-
textarea.style.resize = "none";
|
|
182
|
-
textarea.style.overflow = "hidden";
|
|
183
|
-
textarea.style.whiteSpace = "pre-wrap";
|
|
184
|
-
textarea.style.wordBreak = "break-all";
|
|
185
|
-
textarea.style.zIndex = "2";
|
|
186
|
-
textarea.style.outline = "none";
|
|
187
|
-
|
|
188
|
-
textarea.addEventListener("input", updateHighlight);
|
|
189
|
-
updateHighlight();
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function updateHighlight() {
|
|
193
|
-
if (!textarea) return;
|
|
194
|
-
let code = textarea.value;
|
|
195
|
-
if (code.endsWith("\n")) {
|
|
196
|
-
code += " ";
|
|
197
|
-
}
|
|
198
|
-
block.innerHTML = highlightRuby(code);
|
|
199
|
-
|
|
200
|
-
// Auto-grow height dynamically to fit text
|
|
201
|
-
textarea.style.height = "auto";
|
|
202
|
-
textarea.style.height = textarea.scrollHeight + "px";
|
|
203
|
-
block.style.height = textarea.scrollHeight + "px";
|
|
204
|
-
wrapper.style.height = textarea.scrollHeight + "px";
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Trigger full initialization on mouse hover or click
|
|
208
|
-
wrapper.addEventListener("mouseenter", initializeEditorOnDemand, { once: true });
|
|
209
|
-
wrapper.addEventListener("click", initializeEditorOnDemand);
|
|
210
|
-
|
|
211
|
-
// RDoc-compatible Ruby Syntax Highlighter (Safe placeholder method)
|
|
212
|
-
function highlightRuby(code) {
|
|
213
|
-
let html = code
|
|
214
|
-
.replace(/&/g, "&")
|
|
215
|
-
.replace(/</g, "<")
|
|
216
|
-
.replace(/>/g, ">");
|
|
217
|
-
|
|
218
|
-
const placeholders = [];
|
|
219
|
-
function addPlaceholder(val, className) {
|
|
220
|
-
const id = `__WASM_HL_${placeholders.length}__`;
|
|
221
|
-
placeholders.push({ id, val, className });
|
|
222
|
-
return id;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// 1. Strings (double quotes)
|
|
226
|
-
html = html.replace(/"[^"\\]*(?:\\.[^"\\]*)*"/g, (match) => {
|
|
227
|
-
return addPlaceholder(match, "ruby-string");
|
|
228
|
-
});
|
|
229
|
-
// 2. Strings (single quotes)
|
|
230
|
-
html = html.replace(/'[^'\\]*(?:\\.[^'\\]*)*'/g, (match) => {
|
|
231
|
-
return addPlaceholder(match, "ruby-string");
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
// 3. Comments (safe from backtracking, only comments starting after whitespace or at line start)
|
|
235
|
-
html = html.replace(/(^|\s)(#[^\n]*)$/gm, (match, p1, p2) => {
|
|
236
|
-
return p1 + addPlaceholder(p2, "ruby-comment");
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
// 4. Keywords
|
|
240
|
-
const keywords = /\b(require|do|end|if|else|elsif|unless|while|until|for|in|class|module|def|return|true|false|nil)\b/g;
|
|
241
|
-
html = html.replace(keywords, (match) => {
|
|
242
|
-
return addPlaceholder(match, "ruby-keyword");
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
// 5. Constants (Capitalized names)
|
|
246
|
-
const constants = /\b([A-Z][a-zA-Z0-9_]*)\b/g;
|
|
247
|
-
html = html.replace(constants, (match) => {
|
|
248
|
-
return addPlaceholder(match, "ruby-constant");
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
// 6. Simple numeric values
|
|
252
|
-
const numericValues = /\b(\d+)\b/g;
|
|
253
|
-
html = html.replace(numericValues, (match) => {
|
|
254
|
-
return addPlaceholder(match, "ruby-value");
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
// 7. Specific xlsxrb APIs
|
|
258
|
-
const apis = /\b(generate|add_style|add_sheet|add_row|set_column|set_print_option|border_all|border_bottom|border_top|border_left|border_right|align_horizontal|align_vertical|bold|italic|size|fill_color|number_format)\b/g;
|
|
259
|
-
html = html.replace(apis, (match) => {
|
|
260
|
-
return addPlaceholder(match, "ruby-identifier");
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
// Rehydrate placeholders in reverse order to avoid nested conflicts
|
|
264
|
-
for (let i = placeholders.length - 1; i >= 0; i--) {
|
|
265
|
-
const item = placeholders[i];
|
|
266
|
-
html = html.replace(item.id, `<span class="${item.className}">${item.val}</span>`);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return html;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
// Click: Live Preview (1-click auto run)
|
|
275
|
-
previewBtn.addEventListener("click", () => {
|
|
276
|
-
const codeToRun = textarea ? textarea.value : originalCode;
|
|
277
|
-
triggerLivePreview(codeToRun);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
// Click: Download XLSX directly
|
|
281
|
-
downloadBtn.addEventListener("click", async () => {
|
|
282
|
-
downloadBtn.disabled = true;
|
|
283
|
-
const originalText = downloadBtn.innerHTML;
|
|
284
|
-
downloadBtn.innerHTML = `Running Wasm...`;
|
|
285
|
-
|
|
286
|
-
try {
|
|
287
|
-
const vm = await ensureRubyWasm();
|
|
288
|
-
clearXlsxFiles(vm, "/");
|
|
289
|
-
let userCode = textarea ? textarea.value : originalCode;
|
|
290
|
-
if (!/require\s+['"]xlsxrb['"]/.test(userCode)) {
|
|
291
|
-
userCode = 'require "xlsxrb"\n' + userCode;
|
|
292
|
-
}
|
|
293
|
-
userCode = '$LOAD_PATH << Dir.pwd unless $LOAD_PATH.include?(Dir.pwd)\n' + userCode;
|
|
294
|
-
|
|
295
|
-
vm.eval(userCode);
|
|
296
|
-
const generatedFiles = scanXlsxFiles(vm, "/");
|
|
297
|
-
if (generatedFiles.length > 0) {
|
|
298
|
-
downloadFileFromVfs(vm, generatedFiles[0]);
|
|
299
|
-
} else {
|
|
300
|
-
alert("Error: No XLSX file was generated.");
|
|
301
|
-
}
|
|
302
|
-
} catch (err) {
|
|
303
|
-
alert("Error generating XLSX: " + err.message);
|
|
304
|
-
console.error(err);
|
|
305
|
-
} finally {
|
|
306
|
-
downloadBtn.disabled = false;
|
|
307
|
-
downloadBtn.innerHTML = originalText;
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
// Click: Reset Code
|
|
312
|
-
resetBtn.addEventListener("click", () => {
|
|
313
|
-
if (textarea) {
|
|
314
|
-
textarea.value = originalCode;
|
|
315
|
-
updateHighlight();
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Open the sliding drawer panel and automatically execute code preview inside the iframe
|
|
324
|
-
function triggerLivePreview(code) {
|
|
325
|
-
const drawer = ensurePreviewDrawer();
|
|
326
|
-
const iframe = document.getElementById("wasmPreviewIframe");
|
|
327
|
-
const prefix = window.rdoc_rel_prefix || "./";
|
|
328
|
-
|
|
329
|
-
// Show the drawer
|
|
330
|
-
drawer.classList.add("open");
|
|
331
|
-
|
|
332
|
-
// Initialize or load the iframe
|
|
333
|
-
const targetSrc = `${prefix}preview.html`;
|
|
334
|
-
if (!iframe.src || !iframe.src.includes("preview.html")) {
|
|
335
|
-
iframe.src = targetSrc;
|
|
336
|
-
iframe.onload = () => {
|
|
337
|
-
sendCodeToIframe();
|
|
338
|
-
};
|
|
339
|
-
} else {
|
|
340
|
-
sendCodeToIframe();
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
function sendCodeToIframe() {
|
|
344
|
-
if (iframe.contentWindow) {
|
|
345
|
-
iframe.contentWindow.postMessage({
|
|
346
|
-
action: "load_code",
|
|
347
|
-
code: code
|
|
348
|
-
}, "*");
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Delete all .xlsx files recursively under the virtual directory
|
|
354
|
-
function clearXlsxFiles(vm, dir) {
|
|
355
|
-
try {
|
|
356
|
-
vm.eval(`
|
|
357
|
-
Dir.glob("${dir}/**/*.xlsx").each do |file|
|
|
358
|
-
File.delete(file) rescue nil
|
|
359
|
-
end
|
|
360
|
-
`);
|
|
361
|
-
} catch (e) {
|
|
362
|
-
console.error("Error clearing XLSX files:", e);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Recursively scan virtual directory for generated .xlsx files
|
|
367
|
-
function scanXlsxFiles(vm, dir) {
|
|
368
|
-
try {
|
|
369
|
-
const filesStr = vm.eval(`
|
|
370
|
-
Dir.glob("${dir}/**/*.xlsx").join(",")
|
|
371
|
-
`).toString();
|
|
372
|
-
return filesStr ? filesStr.split(",") : [];
|
|
373
|
-
} catch (e) {
|
|
374
|
-
console.error("Error scanning XLSX files:", e);
|
|
375
|
-
return [];
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Fetch binary contents of a VFS file and trigger browser download
|
|
380
|
-
function downloadFileFromVfs(vm, filepath) {
|
|
381
|
-
try {
|
|
382
|
-
// Hex bridge unpacking to transfer binary data safely to JS
|
|
383
|
-
const hexData = vm.eval(`
|
|
384
|
-
File.binread("${filepath}").unpack1("H*")
|
|
385
|
-
`).toString();
|
|
386
|
-
|
|
387
|
-
const len = hexData.length;
|
|
388
|
-
const bytes = new Uint8Array(len / 2);
|
|
389
|
-
for (let i = 0; i < len; i += 2) {
|
|
390
|
-
bytes[i / 2] = parseInt(hexData.substr(i, 2), 16);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const filename = filepath.split("/").pop();
|
|
394
|
-
const blob = new Blob([bytes], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
|
|
395
|
-
const url = URL.createObjectURL(blob);
|
|
396
|
-
const a = document.createElement("a");
|
|
397
|
-
a.href = url;
|
|
398
|
-
a.download = filename;
|
|
399
|
-
document.body.appendChild(a);
|
|
400
|
-
a.click();
|
|
401
|
-
document.body.removeChild(a);
|
|
402
|
-
setTimeout(() => URL.revokeObjectURL(url), 10000);
|
|
403
|
-
|
|
404
|
-
} catch (e) {
|
|
405
|
-
console.error("Error downloading file from Vfs:", e);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// Create or retrieve the side drawer preview panel
|
|
410
|
-
function ensurePreviewDrawer() {
|
|
411
|
-
let drawer = document.getElementById("wasmPreviewDrawer");
|
|
412
|
-
if (drawer) return drawer;
|
|
413
|
-
|
|
414
|
-
drawer = document.createElement("div");
|
|
415
|
-
drawer.id = "wasmPreviewDrawer";
|
|
416
|
-
drawer.className = "wasm-preview-drawer";
|
|
417
|
-
drawer.innerHTML = `
|
|
418
|
-
<div class="drawer-header">
|
|
419
|
-
<span class="drawer-title">Live Spreadsheet Preview</span>
|
|
420
|
-
<button class="drawer-close-btn">×</button>
|
|
421
|
-
</div>
|
|
422
|
-
<div class="drawer-body">
|
|
423
|
-
<iframe id="wasmPreviewIframe" class="drawer-iframe" src=""></iframe>
|
|
424
|
-
</div>
|
|
425
|
-
`;
|
|
426
|
-
document.body.appendChild(drawer);
|
|
427
|
-
|
|
428
|
-
// Setup close action
|
|
429
|
-
const closeBtn = drawer.querySelector(".drawer-close-btn");
|
|
430
|
-
closeBtn.addEventListener("click", () => {
|
|
431
|
-
drawer.classList.remove("open");
|
|
432
|
-
// Clear iframe src on close to stop background Wasm threads and free memory
|
|
433
|
-
const iframe = document.getElementById("wasmPreviewIframe");
|
|
434
|
-
if (iframe) iframe.src = "";
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
return drawer;
|
|
438
|
-
}
|
|
439
|
-
})();
|