File size: 13,739 Bytes
9bd422a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/**
 * ConversionGuideManager - Manages conversion guides for unsupported model formats
 * Displays instructions to convert various ML model formats to ONNX
 * Requirements: 7.1-7.4, 8.1-8.4, 9.1-9.4, 10.1-10.4, 11.1-11.4, 12.1-12.4, 13.1-13.4, 14.1-14.6
 */

class ConversionGuideManager {
  constructor() {
    this._errorContainer = document.getElementById('errorContainer');
  }

  // ─── Declarative Guide Configs ──────────────────────────────────────────

  static get GUIDE_CONFIGS() {
    return [
      {
        id: 'keras',
        extensions: ['.h5', '.keras'],
        icon: 'fas fa-brain',
        formatName: 'Keras/TensorFlow',
        pipCommand: 'pip install tf2onnx tensorflow',
        codeSnippet: `import tf2onnx
import tensorflow as tf

model = tf.keras.models.load_model("model.h5")

# Convert to ONNX
spec = (tf.TensorSpec(model.input_shape, tf.float32, name="input"),)
output_path = "model.onnx"

model_proto, _ = tf2onnx.convert.from_keras(
    model, input_signature=spec, output_path=output_path
)
print(f"Saved ONNX model to {output_path}")`,
        messages: {
          en: {
            title: 'Keras/TensorFlow file (.h5/.keras) is not directly supported',
            body: 'is a Keras/TensorFlow format. Please convert to <strong>.onnx</strong> before uploading.',
            installNote: 'Install:'
          },
          vi: {
            title: 'Tệp Keras/TensorFlow (.h5/.keras) không được hỗ trợ trực tiếp',
            body: 'là định dạng Keras/TensorFlow. Vui lòng convert sang <strong>.onnx</strong> trước khi upload.',
            installNote: 'Cài đặt:'
          },
          ja: {
            title: 'Keras/TensorFlow ファイル (.h5/.keras) は直接サポートされていません',
            body: 'は Keras/TensorFlow 形式です。アップロードする前に <strong>.onnx</strong> に変換してください。',
            installNote: 'インストール:'
          }
        }
      },
      {
        id: 'tensorflow-savedmodel',
        extensions: ['.pb'],
        icon: 'fas fa-project-diagram',
        formatName: 'TensorFlow SavedModel',
        pipCommand: 'pip install tf2onnx tensorflow',
        codeSnippet: `import tf2onnx
import tensorflow as tf

# Load SavedModel
graph_def, inputs, outputs = tf2onnx.tf_loader.from_saved_model(
    "saved_model_dir"
)

# Convert to ONNX
model_proto, _ = tf2onnx.convert.from_graph_def(
    graph_def,
    input_names=inputs,
    output_names=outputs,
    output_path="model.onnx"
)
print("Saved ONNX model to model.onnx")`,
        messages: {
          en: {
            title: 'TensorFlow SavedModel (.pb) is not directly supported',
            body: 'is a TensorFlow SavedModel format. Please convert to <strong>.onnx</strong> before uploading.',
            installNote: 'Install:'
          },
          vi: {
            title: 'Tệp TensorFlow SavedModel (.pb) không được hỗ trợ trực tiếp',
            body: 'là định dạng TensorFlow SavedModel. Vui lòng convert sang <strong>.onnx</strong> trước khi upload.',
            installNote: 'Cài đặt:'
          },
          ja: {
            title: 'TensorFlow SavedModel (.pb) は直接サポートされていません',
            body: 'は TensorFlow SavedModel 形式です。アップロードする前に <strong>.onnx</strong> に変換してください。',
            installNote: 'インストール:'
          }
        }
      },
      {
        id: 'coreml',
        extensions: ['.mlmodel'],
        icon: 'fas fa-apple-alt',
        formatName: 'CoreML',
        pipCommand: 'pip install coremltools onnxmltools',
        codeSnippet: `import coremltools
import onnxmltools

# Load CoreML model
coreml_model = coremltools.utils.load_spec("model.mlmodel")

# Convert to ONNX
onnx_model = onnxmltools.convert_coreml(coreml_model)
onnxmltools.utils.save_model(onnx_model, "model.onnx")
print("Saved ONNX model to model.onnx")`,
        messages: {
          en: {
            title: 'CoreML file (.mlmodel) is not directly supported',
            body: 'is a CoreML format. Please convert to <strong>.onnx</strong> before uploading.',
            installNote: 'Install:'
          },
          vi: {
            title: 'Tệp CoreML (.mlmodel) không được hỗ trợ trực tiếp',
            body: 'là định dạng CoreML. Vui lòng convert sang <strong>.onnx</strong> trước khi upload.',
            installNote: 'Cài đặt:'
          },
          ja: {
            title: 'CoreML ファイル (.mlmodel) は直接サポートされていません',
            body: 'は CoreML 形式です。アップロードする前に <strong>.onnx</strong> に変換してください。',
            installNote: 'インストール:'
          }
        }
      },
      {
        id: 'caffe',
        extensions: ['.caffemodel'],
        icon: 'fas fa-coffee',
        formatName: 'Caffe',
        pipCommand: 'pip install caffe2onnx',
        codeSnippet: `from caffe2onnx import convertToOnnx

# Convert Caffe model to ONNX
# Requires both .prototxt and .caffemodel files
graph = convertToOnnx("deploy.prototxt", "model.caffemodel")
graph.save("model.onnx")
print("Saved ONNX model to model.onnx")`,
        messages: {
          en: {
            title: 'Caffe file (.caffemodel) is not directly supported',
            body: 'is a Caffe format. Please convert to <strong>.onnx</strong> before uploading.',
            installNote: 'Install:'
          },
          vi: {
            title: 'Tệp Caffe (.caffemodel) không được hỗ trợ trực tiếp',
            body: 'là định dạng Caffe. Vui lòng convert sang <strong>.onnx</strong> trước khi upload.',
            installNote: 'Cài đặt:'
          },
          ja: {
            title: 'Caffe ファイル (.caffemodel) は直接サポートされていません',
            body: 'は Caffe 形式です。アップロードする前に <strong>.onnx</strong> に変換してください。',
            installNote: 'インストール:'
          }
        }
      },
      {
        id: 'darknet',
        extensions: ['.weights'],
        icon: 'fas fa-moon',
        formatName: 'Darknet/YOLO',
        pipCommand: 'pip install torch ultralytics',
        codeSnippet: `from ultralytics import YOLO

# Load Darknet/YOLO weights via Ultralytics
model = YOLO("yolov8n.pt")  # or load custom weights

# Export to ONNX (Darknet -> PyTorch -> ONNX pipeline)
model.export(format="onnx", imgsz=640)
print("Saved ONNX model to yolov8n.onnx")`,
        messages: {
          en: {
            title: 'Darknet/YOLO file (.weights) is not directly supported',
            body: 'is a Darknet/YOLO format. Please convert to <strong>.onnx</strong> via PyTorch before uploading.',
            installNote: 'Install:'
          },
          vi: {
            title: 'Tệp Darknet/YOLO (.weights) không được hỗ trợ trực tiếp',
            body: 'là định dạng Darknet/YOLO. Vui lòng convert sang <strong>.onnx</strong> qua PyTorch trước khi upload.',
            installNote: 'Cài đặt:'
          },
          ja: {
            title: 'Darknet/YOLO ファイル (.weights) は直接サポートされていません',
            body: 'は Darknet/YOLO 形式です。アップロードする前に PyTorch 経由で <strong>.onnx</strong> に変換してください。',
            installNote: 'インストール:'
          }
        }
      },
      {
        id: 'paddlepaddle',
        extensions: ['.pdmodel'],
        icon: 'fas fa-ship',
        formatName: 'PaddlePaddle',
        pipCommand: 'pip install paddle2onnx paddlepaddle',
        codeSnippet: `import paddle2onnx

# Convert PaddlePaddle model to ONNX
paddle2onnx.command.program2onnx(
    model_dir="paddle_model_dir",
    model_filename="model.pdmodel",
    params_filename="model.pdiparams",
    save_file="model.onnx",
    opset_version=13
)
print("Saved ONNX model to model.onnx")`,
        messages: {
          en: {
            title: 'PaddlePaddle file (.pdmodel) is not directly supported',
            body: 'is a PaddlePaddle format. Please convert to <strong>.onnx</strong> before uploading.',
            installNote: 'Install:'
          },
          vi: {
            title: 'Tệp PaddlePaddle (.pdmodel) không được hỗ trợ trực tiếp',
            body: 'là định dạng PaddlePaddle. Vui lòng convert sang <strong>.onnx</strong> trước khi upload.',
            installNote: 'Cài đặt:'
          },
          ja: {
            title: 'PaddlePaddle ファイル (.pdmodel) は直接サポートされていません',
            body: 'は PaddlePaddle 形式です。アップロードする前に <strong>.onnx</strong> に変換してください。',
            installNote: 'インストール:'
          }
        }
      },
      {
        id: 'mxnet',
        extensions: ['.params'],
        icon: 'fas fa-cubes',
        formatName: 'MXNet',
        pipCommand: 'pip install mxnet',
        codeSnippet: `import mxnet as mx
from mxnet.contrib import onnx as onnx_mxnet

# Convert MXNet model to ONNX
# Requires both symbol JSON and params files
converted_model = onnx_mxnet.export_model(
    sym="model-symbol.json",
    params="model-0000.params",
    input_shape=[(1, 3, 224, 224)],
    input_type="float32",
    onnx_file_path="model.onnx"
)
print("Saved ONNX model to model.onnx")`,
        messages: {
          en: {
            title: 'MXNet file (.params) is not directly supported',
            body: 'is an MXNet format. Please convert to <strong>.onnx</strong> before uploading.',
            installNote: 'Install:'
          },
          vi: {
            title: 'Tệp MXNet (.params) không được hỗ trợ trực tiếp',
            body: 'là định dạng MXNet. Vui lòng convert sang <strong>.onnx</strong> trước khi upload.',
            installNote: 'Cài đặt:'
          },
          ja: {
            title: 'MXNet ファイル (.params) は直接サポートされていません',
            body: 'は MXNet 形式です。アップロードする前に <strong>.onnx</strong> に変換してください。',
            installNote: 'インストール:'
          }
        }
      }
    ];
  }

  // ─── Public API ─────────────────────────────────────────────────────────

  /**
   * Check if a filename has a conversion-guide extension.
   * @param {string} fileName
   * @returns {boolean}
   */
  isConversionFormat(fileName) {
    if (!fileName || typeof fileName !== 'string') return false;
    const lower = fileName.toLowerCase();
    return ConversionGuideManager.GUIDE_CONFIGS.some(config =>
      config.extensions.some(ext => lower.endsWith(ext))
    );
  }

  /**
   * Display the conversion guide for the given file.
   * @param {string} fileName
   */
  showGuide(fileName) {
    if (!this._errorContainer) return;

    const lower = (fileName || '').toLowerCase();
    const config = ConversionGuideManager.GUIDE_CONFIGS.find(c =>
      c.extensions.some(ext => lower.endsWith(ext))
    );
    if (!config) return;

    const lang = this._getLanguage();
    const msgs = config.messages[lang] || config.messages['en'];
    const escapedName = this._escapeHtml(fileName);

    this._errorContainer.innerHTML = '';
    const div = document.createElement('div');
    div.className = 'alert alert-warning alert-dismissible fade show';
    div.setAttribute('role', 'alert');
    div.innerHTML = `
      <div class="d-flex align-items-start">
        <i class="fas fa-exchange-alt me-3 mt-1 fs-4 text-warning"></i>
        <div class="flex-grow-1">
          <h6 class="alert-heading mb-2">
            <i class="${config.icon} me-1"></i>
            ${msgs.title}
          </h6>
          <p class="mb-2">
            <strong>"${escapedName}"</strong> ${msgs.body}
          </p>
          <hr class="my-2">
          <p class="mb-1 fw-bold"><i class="fas fa-code me-1"></i> Convert ${config.formatName} → ONNX:</p>
          <pre class="bg-dark text-light p-2 rounded small mb-2" style="white-space:pre-wrap;"><code>${this._escapeHtml(config.codeSnippet)}</code></pre>
          <p class="mb-0 text-muted small">
            <i class="fas fa-info-circle me-1"></i>
            ${msgs.installNote} <code>${config.pipCommand}</code>
          </p>
        </div>
      </div>
      <button type="button" class="btn-close" aria-label="Close"
        onclick="this.closest('.alert').remove()"></button>
    `;
    this._errorContainer.appendChild(div);
  }

  // ─── Private ────────────────────────────────────────────────────────────

  /**
   * Get the current language from localStorage, default to 'en'.
   * @returns {string}
   */
  _getLanguage() {
    try {
      const lang = localStorage.getItem('onnx_explorer_help_lang');
      if (lang && ['en', 'vi', 'ja'].includes(lang)) return lang;
    } catch (_) {
      // localStorage not accessible
    }
    return 'en';
  }

  /**
   * Escape HTML using DOM text node method to prevent XSS.
   * @param {string} str
   * @returns {string}
   */
  _escapeHtml(str) {
    const div = document.createElement('div');
    div.appendChild(document.createTextNode(str || ''));
    return div.innerHTML;
  }
}

// Export as global for browser usage
window.ConversionGuideManager = ConversionGuideManager;