浏览代码

UI simplification and more documentation support

Lutz Roeder 8 年之前
父节点
当前提交
ab752a77d0
共有 19 个文件被更改,包括 4169 次插入518 次删除
  1. 1 0
      package.json
  2. 2 0
      setup.py
  3. 101 55
      src/onnx-operator.json
  4. 68 68
      src/onnx_ml_pb2.py
  5. 3523 0
      src/tf-operator.json
  6. 1 0
      src/view-browser.html
  7. 1 1
      src/view-browser.js
  8. 2 1
      src/view-electron.html
  9. 49 50
      src/view-onnx.js
  10. 0 5
      src/view-render.css
  11. 5 49
      src/view-render.js
  12. 68 174
      src/view-template.js
  13. 207 67
      src/view-tf.js
  14. 20 4
      src/view-tflite.js
  15. 5 10
      src/view.css
  16. 39 29
      src/view.js
  17. 6 5
      tools/onnx-operator-json.py
  18. 3 0
      tools/tf-generate
  19. 68 0
      tools/tf-operator-json.py

+ 1 - 0
package.json

@@ -23,6 +23,7 @@
         "electron-updater": "^2.16.1",
         "flatbuffers": "^1.8.0",
         "handlebars": "x.x.x",
+        "marked": "x.x.x",
         "npm-font-open-sans": "x.x.x",
         "protobufjs": "x.x.x"
     },

+ 2 - 0
setup.py

@@ -24,6 +24,7 @@ package_data={
         'onnx.js',
         'onnx-operator.json',
         'tf.js',
+        'tf-operator.json',
         'tflite.js',
         'tflite-operator.json',
         'favicon.ico',
@@ -49,6 +50,7 @@ custom_files = [
         'node_modules/protobufjs/dist/protobuf.js',
         'node_modules/flatbuffers/js/flatbuffers.js',
         'node_modules/handlebars/dist/handlebars.js',
+        'node_modules/marked/marked.min.js',
         'node_modules/dagre-d3-renderer/dist/dagre-d3.core.js',
         'node_modules/dagre-d3-renderer/dist/dagre-d3.js',
         'node_modules/npm-font-open-sans/open-sans.css' ]),

文件差异内容过多而无法显示
+ 101 - 55
src/onnx-operator.json


+ 68 - 68
src/onnx_ml_pb2.py

@@ -210,91 +210,91 @@ _ATTRIBUTEPROTO = _descriptor.Descriptor(
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='doc_string', full_name='onnx.AttributeProto.doc_string', index=1,
       number=13, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='type', full_name='onnx.AttributeProto.type', index=2,
       number=20, type=14, cpp_type=8, label=1,
       has_default_value=False, default_value=0,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='f', full_name='onnx.AttributeProto.f', index=3,
       number=2, type=2, cpp_type=6, label=1,
       has_default_value=False, default_value=float(0),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='i', full_name='onnx.AttributeProto.i', index=4,
       number=3, type=3, cpp_type=2, label=1,
       has_default_value=False, default_value=0,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='s', full_name='onnx.AttributeProto.s', index=5,
       number=4, type=12, cpp_type=9, label=1,
       has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='t', full_name='onnx.AttributeProto.t', index=6,
       number=5, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='g', full_name='onnx.AttributeProto.g', index=7,
       number=6, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='floats', full_name='onnx.AttributeProto.floats', index=8,
       number=7, type=2, cpp_type=6, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='ints', full_name='onnx.AttributeProto.ints', index=9,
       number=8, type=3, cpp_type=2, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='strings', full_name='onnx.AttributeProto.strings', index=10,
       number=9, type=12, cpp_type=9, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='tensors', full_name='onnx.AttributeProto.tensors', index=11,
       number=10, type=11, cpp_type=10, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='graphs', full_name='onnx.AttributeProto.graphs', index=12,
       number=11, type=11, cpp_type=10, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -326,21 +326,21 @@ _VALUEINFOPROTO = _descriptor.Descriptor(
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='type', full_name='onnx.ValueInfoProto.type', index=1,
       number=2, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='doc_string', full_name='onnx.ValueInfoProto.doc_string', index=2,
       number=3, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -371,49 +371,49 @@ _NODEPROTO = _descriptor.Descriptor(
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='output', full_name='onnx.NodeProto.output', index=1,
       number=2, type=9, cpp_type=9, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='name', full_name='onnx.NodeProto.name', index=2,
       number=3, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='op_type', full_name='onnx.NodeProto.op_type', index=3,
       number=4, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='domain', full_name='onnx.NodeProto.domain', index=4,
       number=7, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='attribute', full_name='onnx.NodeProto.attribute', index=5,
       number=5, type=11, cpp_type=10, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='doc_string', full_name='onnx.NodeProto.doc_string', index=6,
       number=6, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -444,63 +444,63 @@ _MODELPROTO = _descriptor.Descriptor(
       has_default_value=False, default_value=0,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='opset_import', full_name='onnx.ModelProto.opset_import', index=1,
       number=8, type=11, cpp_type=10, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='producer_name', full_name='onnx.ModelProto.producer_name', index=2,
       number=2, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='producer_version', full_name='onnx.ModelProto.producer_version', index=3,
       number=3, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='domain', full_name='onnx.ModelProto.domain', index=4,
       number=4, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='model_version', full_name='onnx.ModelProto.model_version', index=5,
       number=5, type=3, cpp_type=2, label=1,
       has_default_value=False, default_value=0,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='doc_string', full_name='onnx.ModelProto.doc_string', index=6,
       number=6, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='graph', full_name='onnx.ModelProto.graph', index=7,
       number=7, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='metadata_props', full_name='onnx.ModelProto.metadata_props', index=8,
       number=14, type=11, cpp_type=10, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -531,14 +531,14 @@ _STRINGSTRINGENTRYPROTO = _descriptor.Descriptor(
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='value', full_name='onnx.StringStringEntryProto.value', index=1,
       number=2, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -569,49 +569,49 @@ _GRAPHPROTO = _descriptor.Descriptor(
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='name', full_name='onnx.GraphProto.name', index=1,
       number=2, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='initializer', full_name='onnx.GraphProto.initializer', index=2,
       number=5, type=11, cpp_type=10, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='doc_string', full_name='onnx.GraphProto.doc_string', index=3,
       number=10, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='input', full_name='onnx.GraphProto.input', index=4,
       number=11, type=11, cpp_type=10, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='output', full_name='onnx.GraphProto.output', index=5,
       number=12, type=11, cpp_type=10, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='value_info', full_name='onnx.GraphProto.value_info', index=6,
       number=13, type=11, cpp_type=10, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -642,14 +642,14 @@ _TENSORPROTO_SEGMENT = _descriptor.Descriptor(
       has_default_value=False, default_value=0,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='end', full_name='onnx.TensorProto.Segment.end', index=1,
       number=2, type=3, cpp_type=2, label=1,
       has_default_value=False, default_value=0,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -679,84 +679,84 @@ _TENSORPROTO = _descriptor.Descriptor(
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='data_type', full_name='onnx.TensorProto.data_type', index=1,
       number=2, type=14, cpp_type=8, label=1,
       has_default_value=False, default_value=0,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='segment', full_name='onnx.TensorProto.segment', index=2,
       number=3, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='float_data', full_name='onnx.TensorProto.float_data', index=3,
       number=4, type=2, cpp_type=6, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\020\001'))),
+      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\020\001')), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='int32_data', full_name='onnx.TensorProto.int32_data', index=4,
       number=5, type=5, cpp_type=1, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\020\001'))),
+      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\020\001')), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='string_data', full_name='onnx.TensorProto.string_data', index=5,
       number=6, type=12, cpp_type=9, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='int64_data', full_name='onnx.TensorProto.int64_data', index=6,
       number=7, type=3, cpp_type=2, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\020\001'))),
+      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\020\001')), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='name', full_name='onnx.TensorProto.name', index=7,
       number=8, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='doc_string', full_name='onnx.TensorProto.doc_string', index=8,
       number=12, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='raw_data', full_name='onnx.TensorProto.raw_data', index=9,
       number=9, type=12, cpp_type=9, label=1,
       has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='double_data', full_name='onnx.TensorProto.double_data', index=10,
       number=10, type=1, cpp_type=5, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\020\001'))),
+      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\020\001')), file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='uint64_data', full_name='onnx.TensorProto.uint64_data', index=11,
       number=11, type=4, cpp_type=4, label=3,
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\020\001'))),
+      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\020\001')), file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -788,14 +788,14 @@ _TENSORSHAPEPROTO_DIMENSION = _descriptor.Descriptor(
       has_default_value=False, default_value=0,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='dim_param', full_name='onnx.TensorShapeProto.Dimension.dim_param', index=1,
       number=2, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -828,7 +828,7 @@ _TENSORSHAPEPROTO = _descriptor.Descriptor(
       has_default_value=False, default_value=[],
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -859,14 +859,14 @@ _TYPEPROTO_TENSOR = _descriptor.Descriptor(
       has_default_value=False, default_value=0,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='shape', full_name='onnx.TypeProto.Tensor.shape', index=1,
       number=2, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -896,7 +896,7 @@ _TYPEPROTO_SEQUENCE = _descriptor.Descriptor(
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -926,14 +926,14 @@ _TYPEPROTO_MAP = _descriptor.Descriptor(
       has_default_value=False, default_value=0,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='value_type', full_name='onnx.TypeProto.Map.value_type', index=1,
       number=2, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -963,21 +963,21 @@ _TYPEPROTO = _descriptor.Descriptor(
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='sequence_type', full_name='onnx.TypeProto.sequence_type', index=1,
       number=4, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='map_type', full_name='onnx.TypeProto.map_type', index=2,
       number=5, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -1011,14 +1011,14 @@ _OPERATORSETIDPROTO = _descriptor.Descriptor(
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
       name='version', full_name='onnx.OperatorSetIdProto.version', index=1,
       number=2, type=3, cpp_type=2, label=1,
       has_default_value=False, default_value=0,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
-      options=None),
+      options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],

文件差异内容过多而无法显示
+ 3523 - 0
src/tf-operator.json


+ 1 - 0
src/view-browser.html

@@ -28,6 +28,7 @@
 <script type='text/javascript' src='protobuf.js'></script>
 <script type='text/javascript' src='flatbuffers.js'></script>
 <script type='text/javascript' src='handlebars.js'></script>
+<script type='text/javascript' src='marked.min.js'></script>
 <script type='text/javascript' src='onnx.js'></script>
 <script type='text/javascript' src='tf.js'></script>
 <script type='text/javascript' src='tflite.js'></script>

+ 1 - 1
src/view-browser.js

@@ -9,7 +9,7 @@ class BrowserHostService {
         this.callback = callback;
     
         updateView('spinner');
-        
+
         var request = new XMLHttpRequest();
         request.responseType = 'arraybuffer';
         request.onload = () => {

+ 2 - 1
src/view-electron.html

@@ -26,7 +26,8 @@
 <script type='text/javascript' src='../node_modules/dagre-d3-renderer/dist/dagre-d3.js'></script>
 <script type='text/javascript' src='../node_modules/protobufjs/dist/protobuf.js'></script>
 <script type='text/javascript' src='../node_modules/flatbuffers/js/flatbuffers.js'></script>
-<script type='text/javascript' src='../node_modules/handlebars/dist/handlebars.js'></script>
+<script type='text/javascript' src='../node_modules/handlebars/dist/handlebars.min.js'></script>
+<script type='text/javascript' src='../node_modules/marked/marked.min.js'></script>
 <script type='text/javascript' src='onnx.js'></script>
 <script type='text/javascript' src='tf.js'></script>
 <script type='text/javascript' src='tflite.js'></script>

+ 49 - 50
src/view-onnx.js

@@ -18,8 +18,8 @@ class OnnxModel {
                 this._activeGraph = this._graph;
             }
 
-            if (!this._operatorMetadata) {
-                this._operatorMetadata = new OnnxOperatorMetadata(this.hostService);
+            if (!OnnxModel.operatorMetadata) {
+                OnnxModel.operatorMetadata = new OnnxOperatorMetadata(this.hostService);
             }
         }
         catch (err) {
@@ -91,6 +91,17 @@ class OnnxGraph {
         this._model = model;
         this._graph = graph;
         this._name = this._graph.name ? this._graph.name : ('(' + index.toString() + ')');
+            
+        this._outputMap = {};
+        this._graph.node.forEach((node) => {
+            node.output.forEach((output) => {
+                var count = this._outputMap[output];
+                if (!count) {
+                    count = 0;
+                }
+                this._outputMap[output] = count + 1;
+            });
+        });
     }
 
     get model() {
@@ -145,7 +156,7 @@ class OnnxGraph {
                 this._initializers.push(new OnnxTensor(tensor, tensor.name, 'Initializer'));
             });
             this._graph.node.forEach((node) => {
-                if (node.opType == 'Constant' && node.output && node.output.length == 1) {
+                if (node.opType == 'Constant' && node.output && node.output.length == 1 && this._outputMap[node.output[0]] == 1) {
                     node.attribute.forEach((attribute) => {
                         if (attribute.name == 'value' && attribute.t) {
                             this._initializers.push(new OnnxTensor(attribute.t, node.output[0], 'Constant'));
@@ -164,7 +175,7 @@ class OnnxGraph {
             initializerMap[initializer.id] = true;
         });
         this._graph.node.forEach((node) => {
-            if (node.opType == 'Constant' && node.output.length == 1 && initializerMap[node.output[0]]) {
+            if (initializerMap[node.output[0]]) {
 
             }
             else {
@@ -182,77 +193,71 @@ class OnnxNode {
         this._node = node;
     }
 
+    get operator() {
+        return this._node.opType;
+    }
+
+    get name() {
+        return this._node.name ? this._node.name : null;
+    }
+
+    get description() {
+        return this._node.docString ? this._node.docString : null;
+    }
+
+    get primitive() {
+        return null;
+    }
+
+    get constant() {
+        return this._node.opType == 'Constant';
+    }
+
     get documentation() {
-        return this._graph.model._operatorMetadata.getOperatorDocumentation(this.operator);
+        return OnnxModel.operatorMetadata.getOperatorDocumentation(this.operator);
     }
 
-    get operator() {
-        return this._node.opType;
+    get domain() {
+        return this._node.domain ? this._node.domain : null;
     }
 
     get inputs() {
-        var operatorMetadata = this._graph.model._operatorMetadata;
         var results = [];
         this._node.input.forEach((input, index) => {
             results.push({
                 'id': input,
-                'name': operatorMetadata.getInputName(this.operator, index),
-                'type': ""
+                'name': OnnxModel.operatorMetadata.getInputName(this.operator, index),
+                'type': ''
             });
         });
         return results;
     }
 
     get outputs() {
-        var operatorMetadata = this._graph.model._operatorMetadata;
         var results = [];
         this._node.output.forEach((output, index) => {
             results.push({
                 id: output,
-                name: operatorMetadata.getOutputName(this.operator, index),
+                name: OnnxModel.operatorMetadata.getOutputName(this.operator, index),
                 type: ''
             });
         });
         return results;
     }
 
-    get properties() {
-        var result = null;
-        var node = this._node;
-        if (node.name || node.docString || node.domain) {
-            result = [];
-            if (node.name) {
-                result.push({ name: 'name', value: node.name, value_short: () => { return node.name; } });
-            }
-            if (node.docString) {
-                result.push({ name: 'doc', value: node.docString, value_short: () => {
-                    var value = node.docString;
-                    if (value.length > 50) {
-                        return value.substring(0, 25) + '...';
-                    }
-                    return value;
-                } });
-            }
-            if (node.domain) {
-                result.push({ name: 'domain', value: node.domain, value_short: () => { return node.domain; } });
-            }        
-        }
-        return result;
-    }
-
     get attributes() {
         var result = null;
         var node = this._node;
         if (node.attribute && node.attribute.length > 0) {
             result = [];
             node.attribute.forEach((attribute) => { 
-                result.push(this.formatNodeAttribute(attribute));
+                result.push(this.formatAttribute(attribute));
             });
         }
         return result;
     }
 
-    formatNodeAttribute(attribute) {
+    formatAttribute(attribute) {
         var type = "";
         if (attribute.hasOwnProperty('type')) { 
             type = OnnxTensor.formatElementType(attribute.type);
@@ -346,7 +351,7 @@ class OnnxNode {
             return value;
         };
         if (attribute.docString) {
-            result.doc = attribute.docString;
+            result.description = attribute.docString;
         }
 
         return result;
@@ -387,12 +392,6 @@ class OnnxTensor {
             return 'Tensor has no dimensions.';
         }
 
-        var size = 1;
-        this._tensor.dims.forEach((dimSize) => { size *= dimSize; });
-        if (size > 65536) {
-            return 'Tensor is too large to display.';
-        }
-
         switch (this._tensor.dataType) {
             case onnx.TensorProto.DataType.FLOAT:
                 if (this._tensor.floatData && this._tensor.floatData.length > 0) {
@@ -665,8 +664,8 @@ class OnnxOperatorMetadata {
         if (schema) {
             schema = Object.assign({}, schema);
             schema.name = operator;
-            if (schema.doc) {
-                var input = schema.doc.split('\n');
+            if (schema.description) {
+                var input = schema.description.split('\n');
                 var output = [];
                 var lines = [];
                 var code = true;
@@ -685,8 +684,8 @@ class OnnxOperatorMetadata {
                             }
                             else {
                                 var text = lines.join('');
-                                text += text.replace(/\`\`(.*?)\`\`/gm, (match, content) => '<code>' + content + '</code>');
-                                text += text.replace(/\`(.*?)\`/gm, (match, content) => '<code>' + content + '</code>');
+                                text = text.replace(/\`\`(.*?)\`\`/gm, (match, content) => '<code>' + content + '</code>');
+                                text = text.replace(/\`(.*?)\`/gm, (match, content) => '<code>' + content + '</code>');
                                 output.push('<p>' + text + '</p>');
                             }
                         }
@@ -694,7 +693,7 @@ class OnnxOperatorMetadata {
                         code = true;
                     }
                 }
-                schema.doc = output.join('');
+                schema.description = output.join('');
             }
             var formatRange = (value) => {
                 return (value == 2147483647) ? '&#8734;' : value.toString();

+ 0 - 5
src/view-render.css

@@ -14,11 +14,6 @@
 .node-item-constant:hover { cursor: hand; }
 .node-item-constant:hover path { fill: #fff; }
 
-.node-property:hover { cursor: hand; }
-.node-property text { font-family: 'Open Sans', --apple-system, "Helvetica Neue", Helvetica, Arial, sans-serf; font-size: 7px; font-weight: normal; text-rendering: geometricPrecision; }
-.node-property path { fill: #fff; stroke-width: 0; stroke: #000; }
-.node-property:hover path { fill: #f6f6f6; }
-
 .node-attribute:hover { cursor: hand; }
 .node-attribute text { font-family: 'Open Sans', --apple-system, "Helvetica Neue", Helvetica, Arial, sans-serf; font-size: 7px; font-weight: normal; text-rendering: geometricPrecision; }
 .node-attribute path { fill: #fff; stroke-width: 0; stroke: #000; }

+ 5 - 49
src/view-render.js

@@ -4,7 +4,6 @@ class NodeFormatter {
 
     constructor(context) {
         this.items = [];
-        this.properties = [];
         this.attributes = [];
     }
 
@@ -25,14 +24,6 @@ class NodeFormatter {
         this.items.push(item);
     }
 
-    addProperty(name, value) {
-        this.properties.push({ name: name, value: value });
-    }
-
-    setPropertyHandler(handler) {
-        this.propertyHandler = handler;
-    }
-
     addAttribute(name, value, title) {
         this.attributes.push({ name: name, value: value, title: title });
     }
@@ -43,7 +34,6 @@ class NodeFormatter {
 
     format(context) {
         var root = context.append('g');
-        var hasProperties = this.properties && this.properties.length > 0;
         var hasAttributes = this.attributes && this.attributes.length > 0;
         var x = 0;
         var y = 0;
@@ -95,36 +85,6 @@ class NodeFormatter {
         x = 0;
         y += itemHeight;
 
-        var propertiesHeight = 0;
-        var propertiesPath = null;
-        if (hasProperties) {
-            var propertyGroup = root.append('g').classed('node-property', true);
-            if (this.propertyHandler) {
-                propertyGroup.on('click', this.propertyHandler);
-            }
-            propertiesPath = propertyGroup.append('path');
-            propertyGroup.attr('transform', 'translate(' + x + ',' + y + ')');
-            propertiesHeight += 4;
-            this.properties.forEach((property) => {
-                var yPadding = 1;
-                var xPadding = 4;
-                var text = propertyGroup.append('text').attr('xml:space', 'preserve');
-                var text_name = text.append('tspan').style('font-weight', 'bold').text(property.name);
-                var text_value = text.append('tspan').text(': ' + property.value);
-                var size = text.node().getBBox();
-                var width = xPadding + size.width + xPadding;
-                if (maxWidth < width) {
-                    maxWidth = width;
-                }
-                text.attr('x', x + xPadding);
-                text.attr('y', propertiesHeight + yPadding - size.y);
-                propertiesHeight += yPadding + size.height + yPadding;
-            });
-            propertiesHeight += hasAttributes ? 1 : 4;
-        }
-
-        y += propertiesHeight;
-
         var attributesHeight = 0;
         var attributesPath = null;
         if (hasAttributes)
@@ -135,7 +95,7 @@ class NodeFormatter {
             }
             attributesPath = attributeGroup.append('path');
             attributeGroup.attr('transform', 'translate(' + x + ',' + y + ')');
-            attributesHeight += hasProperties ? 1 : 4;
+            attributesHeight += 4;
             this.attributes.forEach((attribute) => {
                 var yPadding = 1;
                 var xPadding = 4;
@@ -170,16 +130,12 @@ class NodeFormatter {
             itemBox.group.attr('transform', 'translate(' + itemBox.x + ',' + itemBox.y + ')');        
             var r1 = index == 0;
             var r2 = index == itemBoxes.length - 1;
-            var r3 = !hasAttributes && !hasProperties && r2;
-            var r4 = !hasAttributes && !hasProperties && r1;
+            var r3 = !hasAttributes && r2;
+            var r4 = !hasAttributes && r1;
             itemBox.path.attr('d', this.roundedRect(0, 0, itemBox.width, itemBox.height, r1, r2, r3, r4));
             itemBox.text.attr('x', itemBox.tx).attr('y', itemBox.ty);
         });
 
-        if (hasProperties) {
-            propertiesPath.attr('d', this.roundedRect(0, 0, maxWidth, propertiesHeight, false, false, !hasAttributes, !hasAttributes));
-        }
-
         if (hasAttributes) {
             attributesPath.attr('d', this.roundedRect(0, 0, maxWidth, attributesHeight, false, false, true, true));
         }
@@ -189,10 +145,10 @@ class NodeFormatter {
                 root.append('line').classed('node', true).attr('x1', itemBox.x).attr('y1', 0).attr('x2', itemBox.x).attr('y2', itemHeight);
             }
         });
-        if (hasAttributes || hasProperties) {
+        if (hasAttributes) {
             root.append('line').classed('node', true).attr('x1', 0).attr('y1', itemHeight).attr('x2', maxWidth).attr('y2', itemHeight);
         }
-        root.append('path').classed('node', true).attr('d', this.roundedRect(0, 0, maxWidth, itemHeight + propertiesHeight + attributesHeight, true, true, true, true));
+        root.append('path').classed('node', true).attr('d', this.roundedRect(0, 0, maxWidth, itemHeight + attributesHeight, true, true, true, true));
 
         context.html("");
         return root;

+ 68 - 174
src/view-template.js

@@ -2,51 +2,16 @@
 
 var itemsTemplate = `
 <style type='text/css'>
-
-.items {
-    font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
-    font-size: 12px;
-    line-height: 1.5;
-    margin: 0;
-}
-.item {
-    margin-bottom: 20px;
-}
-.item b {
-    font-weight: 600;
-}
-.item h1 {
-    font-weight: 600;
-    font-size: 14px;
-    line-height: 1.25;
-    border-bottom: 1px solid #eaecef;
-    padding-bottom: 0.3em;
-    margin-top: 0;
-    margin-bottom: 16px;
-}
-.item code {
-    font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
-    font-size: 10px;
-    background-color: rgba(27, 31, 35, 0.05);
-    padding: 0.2em 0.4em;
-    margin: 0;
-    border-radius: 3px
-}
-.item pre {
-    font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
-    font-size: 11px;
-    padding: 8px 12px 8px 12px;
-    overflow: auto;
-    line-height: 1.45;
-    background-color: rgba(27, 31, 35, 0.05);
-    border-radius: 3px;
-    white-space: pre-wrap;
-    word-wrap: break-word;  
-}
+.items { font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 12px; line-height: 1.5; margin: 0; }
+.item { margin-bottom: 20px; }
+.item b { font-weight: 600; }
+.item h1 { font-weight: 600; font-size: 14px; line-height: 1.25; border-bottom: 1px solid #eaecef; padding-bottom: 0.3em; margin-top: 0; margin-bottom: 16px; }
+.item code { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 10px; background-color: rgba(27, 31, 35, 0.05); padding: 0.2em 0.4em; margin: 0; border-radius: 3px }
+.item pre { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 11px; padding: 8px 12px 8px 12px; overflow: auto; line-height: 1.45; background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; white-space: pre-wrap; word-wrap: break-word; }
 </style>
 <div class='items'>
 {{#items}}
-<div class='item'>    
+<div class='item'>
 <b>{{{name}}}{{#if type}}: {{/if}}</b>{{#if type}}<code>{{{type}}}</code>{{/if}}<br>
 {{#if quantization}}
 <pre>{{{quantization}}}</pre>
@@ -62,78 +27,27 @@ var itemsTemplate = `
 
 var operatorTemplate = `
 <style type='text/css'>
-
-.documentation {
-    font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
-    font-size: 12px;
-    line-height: 1.5;
-    margin: 0;
-}
-.documentation h1 {
-    font-weight: 600;
-    font-size: 14px;
-    line-height: 1.25;
-    border-bottom: 1px solid #eaecef;
-    padding-bottom: 0.3em;
-    margin-top: 0;
-    margin-bottom: 16px;
-}
-.documentation h2 {
-    font-weight: 600;
-    font-size: 12px;
-    line-height: 1.25;
-    margin-bottom: 16px;
-    border-bottom: 1px solid #eaecef
-}
-.documentation h3 {
-    font-weight: 600;
-    font-size: 12px;
-    line-height: 1.25;
-}
-.documentation code {
-    font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
-    font-size: 10px;
-    background-color: rgba(27, 31, 35, 0.05);
-    padding: 0.2em 0.4em;
-    margin: 0;
-    border-radius: 3px
-}
-.documentation pre {
-    font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
-    font-size: 11px;
-    padding: 16px;
-    overflow: auto;
-    line-height: 1.45;
-    background-color: rgba(27, 31, 35, 0.05);
-    border-radius: 3px
-}
-.documentation tt {
-    font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
-    font-weight: 600;
-    font-size: 85%;
-    background-color: rgba(27, 31, 35, 0.05);
-    border-radius: 3px;
-    padding: 0.2em 0.4em;
-    margin: 0;
-}
-.documentation dl dt {
-    font-size: 12px;
-    font-weight: 600;
-    padding: 0;
-    margin-top: 16px;
-}
-.documentation dd {
-    padding: 0 16px;
-    margin-left: 0;
-    margin-bottom: 16px;
-}
+.documentation { font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 12px; line-height: 1.5; margin: 0; }
+.documentation h1 { font-weight: 600; font-size: 14px; line-height: 1.25; border-bottom: 1px solid #eaecef; padding-bottom: 0.3em; margin-top: 0; margin-bottom: 16px; }
+.documentation h2 { font-weight: 600; font-size: 12px; line-height: 1.25; margin-bottom: 16px; border-bottom: 1px solid #eaecef; }
+.documentation h3 { font-weight: 600; font-size: 12px; line-height: 1.25; }
+.documentation p { }
+.documentation code { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 10px; background-color: rgba(27, 31, 35, 0.05); padding: 0.2em 0.4em; margin: 0; border-radius: 3px }
+.documentation pre { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 11px; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; }
+.documentation pre code { font-size: 11px; padding: 16px; line-height: 1.45; background-color: transparent; padding: 0; border-radius: 0; }
+.documentation tt { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-weight: 600; font-size: 85%; background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; padding: 0.2em 0.4em; margin: 0; }
+.documentation dl dt { font-size: 12px; font-weight: 600; padding: 0; margin-top: 16px; }
+.documentation dd { padding: 0 16px; margin-left: 0; margin-bottom: 16px; }
 </style>
 
 <div class='documentation'>
 
 <h1>{{{name}}}</h1>
-{{#if doc}}
-{{{doc}}}
+{{#if summary}}
+<p>{{{summary}}}</p>
+{{/if}}
+{{#if description}}
+<p>{{{description}}}</p>
 {{/if}}
 
 {{#if attributes}}
@@ -196,71 +110,13 @@ In domain <tt>{{{domain}}}</tt> since version <tt>{{{since_version}}}</tt> at su
 
 var summaryTemplate = `
 <style type='text/css'>
-.summary {
-    font-family: 'Open Sans', --apple-system, "Helvetica Neue", Helvetica, Arial, sans-serf;
-    font-size: 12px;
-    line-height: 1.5;
-    overflow: hidden;
-    width: 500;
-    margin: auto;
-}
-
-.summary h1 {
-    font-family: 'Open Sans', --apple-system, "Helvetica Neue", Helvetica, Arial, sans-serf;
-    font-weight: 600;
-    font-size: 12px;
-    margin: 0;
-    color: #666;
-    letter-spacing: 0.5px;
-    padding: 10px 0px 0px 0px;
-    margin: 20px 0px 0px 0px;
-    -webkit-user-select: none;
-    -moz-user-select: none;
-    user-select: none;
-}
-
-.summary .section {
-    margin-top: 10px;
-    margin-bottom: 10px;
-    padding: 10px;
-    overflow-y: auto;
-    position: relative;
-    border: 1px solid none;
-}
-
-.summary .section table code {
-    font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
-    font-size: 10px;
-    background-color: rgba(27, 31, 35, 0.05);
-    padding: 0.2em 0.4em;
-    margin: 0;
-    border-radius: 3px;
-    background-color: #d6d6d6;
-}
-
-.summary .border {
-    border-radius: 10px;
-    border: 1px solid #ccc;
-}
-
-.summary .section table { 
-    color: #777;
-    float: left;
-    font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
-    font-size: 12px;
-    border-spacing: 0;
-    line-height: 150%;
-}
-
-.summary .section table td:first-child {
-    font-weight: 600;
-    width: 75px;
-    vertical-align: top;
-    user-select: none;
-    -webkit-user-select: none;
-    -moz-user-select: none;
-}
-
+.summary { font-family: 'Open Sans', --apple-system, "Helvetica Neue", Helvetica, Arial, sans-serf; font-size: 12px; line-height: 1.5; overflow: hidden; width: 500; margin: auto; }
+.summary h1 { font-family: 'Open Sans', --apple-system, "Helvetica Neue", Helvetica, Arial, sans-serf; font-weight: 600; font-size: 12px; margin: 0; color: #666; letter-spacing: 0.5px; padding: 10px 0px 0px 0px; margin: 20px 0px 0px 0px; -webkit-user-select: none; -moz-user-select: none; user-select: none; }
+.summary .section { margin-top: 10px; margin-bottom: 10px; padding: 10px; overflow-y: auto; position: relative; border: 1px solid none; }
+.summary .section table code { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 10px; background-color: rgba(27, 31, 35, 0.05); padding: 0.2em 0.4em; margin: 0; border-radius: 3px; background-color: #d6d6d6; }
+.summary .border { border-radius: 10px; border: 1px solid #ccc; }
+.summary .section table { color: #777; float: left; font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 12px; border-spacing: 0; line-height: 150%; }
+.summary .section table td:first-child { font-weight: 600; width: 75px; vertical-align: top; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
 .summary .section table td { user-select: text; -webkit-user-select: text; -moz-user-select: text; }
 </style>
 <div class='summary'>
@@ -313,4 +169,42 @@ var summaryTemplate = `
 </div>
 {{/graphs}}
 </div>
+`;
+
+var nodeTemplate = `
+<style type='text/css'>
+.details h1 { font-weight: 600; font-size: 14px; line-height: 1.25; border-bottom: 1px solid #eaecef; padding-bottom: 0.3em; margin-top: 0; margin-bottom: 16px; }
+.details .items { font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 12px; line-height: 1.5; margin: 0; }
+.details .item { margin-bottom: 20px; }
+.details .item b { font-weight: 600; }
+.details .item code { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 10px; background-color: rgba(27, 31, 35, 0.05); padding: 0.2em 0.4em; margin: 0; border-radius: 3px }
+.details .item pre { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 11px; padding: 8px 12px 8px 12px; overflow: auto; line-height: 1.45; background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; white-space: pre-wrap; word-wrap: break-word; }
+</style>
+<div class='details'>
+<div class='items'>
+{{#if name}}
+<div class='item'><b>name</b><br><pre>{{{name}}}</pre></div>
+{{/if}}
+{{#if description}}
+<div class='item'><b>description</b><br><pre>{{{description}}}</pre></div>
+{{/if}}
+{{#if domain}}
+<div class='item'><b>domain</b><br><pre>{{{domain}}}</pre></div>
+{{/if}}
+</div>
+{{#if attributes}}
+<h1>Attributes</h1>
+{{/if}}
+<div class='items'>
+{{#attributes}}
+<div class='item'>    
+<b>{{{name}}}{{#if type}}: {{/if}}</b>{{#if type}}<code>{{{type}}}</code>{{/if}}<br>
+{{#if description}}
+{{{description}}}
+{{/if}}
+<pre>{{{value}}}</pre>
+</div>
+{{/attributes}}
+</div>
+</div>
 `;

+ 207 - 67
src/view-tf.js

@@ -33,7 +33,11 @@ class TensorFlowModel {
                 this._format = 'TensorFlow Graph Defintion';
             }
 
-            this._activeGraph = (this._graphs.length > 0) ? this._graphs[0] : null;            
+            this._activeGraph = (this._graphs.length > 0) ? this._graphs[0] : null;
+
+            if (!TensorFlowModel.operatorMetadata) {
+                TensorFlowModel.operatorMetadata = new TensorFlowOperatorMetadata(hostService);
+            }
         }
         catch (err) {
             return err;
@@ -83,9 +87,69 @@ class TensorFlowGraph {
         this._graph = graph;
         this._name = this._graph.anyInfo ? this._graph.anyInfo.toString() : ('(' + index.toString() + ')');
 
+        this._nodeMap = {};
+        var nodes = this._graph.graphDef.node;
+        nodes.forEach((node) => {
+            this._nodeMap[node.name] = node;   
+            node.output = [];         
+        });
+        nodes.forEach((node) => {
+            for (var i = 0; i < node.input.length; i++)
+            {
+                var split = node.input[i].split(':', 1);
+                if (split.length == 1) {
+                    split.push('0');
+                }
+                // TODO
+                if (split[0].startsWith('^')) {
+                    split[0] = split[0].substring(1);
+                }
+                var outputName = split[0];
+                var outputIndex = parseInt(split[1]);
+                var outputNode = this._nodeMap[outputName];
+                node.input[i] = outputName + ':' + outputIndex.toString();
+                if (outputNode) {
+                    for (var j = outputNode.output.length; j <= outputIndex; j++) {
+                        outputNode.output.push('');
+                    }
+                    outputNode.output[outputIndex] = node.input[i];
+                }
+            }
+        });
         this._outputMap = {};
+        nodes.forEach((node) => {
+            node.output.forEach((output) => {
+                var count = this._outputMap[output];
+                if (!count) {
+                    count = 0;
+                }
+                this._outputMap[output] = count + 1;
+            });
+        });
+
+        this._initializerMap = {};
+        this._graph.graphDef.node.forEach((node) => {
+            if (this.checkNode(node, 'Const', 0, 1)) {
+                var tensor = null;
+                Object.keys(node.attr).forEach((name) => {
+                    if (name == 'value') {
+                        tensor = node.attr[name];
+                        return;
+                    }
+                });
+                this._initializerMap[node.output[0]] = new TensorFlowTensor(tensor, node.output[0], 'Constant');
+            }
+        });
         this._graph.graphDef.node.forEach((node) => {
-            
+            if (this.checkNode(node, 'Identity', 1, 1)) {
+                var tensor = this._initializerMap[node.input[0]];
+                if (tensor) {
+                    this._initializerMap[node.input[0]] = "-";
+                    tensor._id = node.output[0]; // TODO update tensor id
+                    tensor._title = 'Constant Identity';
+                    this._initializerMap[node.output[0]] = tensor;
+                }
+            }
         });
     }
 
@@ -121,20 +185,10 @@ class TensorFlowGraph {
 
     get initializers() {
         var results = [];
-        this._graph.graphDef.node.forEach((node) => {
-            if (node.op == 'Const') {
-                var id = node.name;
-                if (id.indexOf(':') == -1) {
-                    id += ':0';
-                }
-                var tensor = null;
-                Object.keys(node.attr).forEach((name) => {
-                    if (name == 'value') {
-                        tensor = node.attr[name];
-                        return;
-                    }
-                });
-                results.push(new TensorFlowTensor(tensor, id, node.name));
+        Object.keys(this._initializerMap).forEach((key) => {
+            var value = this._initializerMap[key];
+            if (value != '-') {
+                results.push(value);
             }
         });
         return results;
@@ -146,7 +200,7 @@ class TensorFlowGraph {
         // });
         var results = [];
         this._graph.graphDef.node.forEach((node) => {
-            if (node.op != 'Const') {
+            if (!this._initializerMap[node.name + ':0']) {
                 results.push(new TensorFlowNode(this, node));
             }
         });
@@ -155,10 +209,30 @@ class TensorFlowGraph {
 
     get metadata() {
         if (!this._metadata) {
-            this._metadata = new TensorFlowGraphMetadata(this._graph.metaInfoDef);
+            this._metadata = new TensorFlowGraphOperatorMetadata(this._graph.metaInfoDef);
         }
         return this._metadata;
     }
+
+    checkNode(node, operator, inputs, outputs) {
+        if (node.op != operator) {
+            return false;
+        }
+        if (outputs == 0 && node.output.length != 0) {
+            return false;
+        }
+        if (inputs == 0 && node.input.length != 0) {
+            return false;
+        }
+        if (outputs > 0 && node.output.length != 1 && this._outputMap[node.output[0] != outputs]) {
+            return false;
+        }
+        if (inputs > 0 && node.input.length != 1 && this._outputMap[node.input[0] != inputs]) {
+            return false;
+        }
+        return true;
+    }
+
 }
 
 class TensorFlowNode {
@@ -172,54 +246,72 @@ class TensorFlowNode {
         return this._node.op;
     }
 
+    get name() {
+        return this._node.name;
+    }
+
+    get description() {
+        return null;
+    }
+
+    get primitive() {
+        /*
+        switch (this._node.op) {
+            case 'Add': return '+';
+            case 'Mul': return '*';
+            case 'Sub': return '-';
+            case 'Identity': return 'I';
+        }
+        */
+        return null;
+    }
+
+    get constant() {
+        return this._node.op == 'Const';
+    }
+
+    get documentation() {
+        var graphMetadata = this._graph.metadata;
+        if (graphMetadata) {
+            return graphMetadata.getOperatorDocumentation(this.operator);       
+        }
+        return null;
+    }
+
+    get domain() {
+        return null;
+    }
+
     get inputs() {
         var graphMetadata = this._graph.metadata;
         var node = this._node;
-        var result = [];
+        var results = [];
         if (node.input) {
-            node.input.forEach(function(input, index) {
-                if (input.startsWith('^')) {
-                    input = input.substring(1);
-                }
-                if (input.indexOf(':') == -1) {
-                    input += ':0';
-                }
-                result.push({
+            node.input.forEach((input, index) => {
+                results.push({
                     'id': input, 
                     'name': graphMetadata ? graphMetadata.getInputName(node.op, index) : ('(' + index.toString() + ')'),
                     'type': ''
                 });
             });
         }
-        return result;
+        return results;
     }
 
     get outputs() {
         var graphMetadata = this._graph.metadata;
         var node = this._node;
-        var index = 0;
-        var id = node.name + ':0';
-        return [ { 
-            'id': id,
-            'name': graphMetadata ? graphMetadata.getOutputName(node.op, index) : ('(' + index.toString() + ')'),
-            'type': ''
-        } ];
-    }
-
-    get properties() {
-        var node = this._node;
-        var result = null;
-        if (node.name) {
-            result = [];
-            if (node.name) {
-                result.push({
-                    'name': 'name', 
-                    'value': node.name,
-                    'value_short': function() { return node.name; } 
+        var results = [];
+        if (node.output) {
+            node.output.forEach((output, index) => {
+                results.push({
+                    'id': output, 
+                    'name': graphMetadata ? graphMetadata.getOutputName(node.op, index) : ('(' + index.toString() + ')'),
+                    'type': ''
                 });
-            }
+            });
         }
-        return result;
+        return results;
     }
 
     get attributes() {
@@ -227,7 +319,7 @@ class TensorFlowNode {
         var result = [];
         if (node.attr) {
             Object.keys(node.attr).forEach(function (name) {
-                if (name != '_output_shapes') {
+                if (name != '_output_shapes' && name != 'T') {
                     var value = node.attr[name];
                     result.push({ 
                         'name': name,
@@ -240,21 +332,16 @@ class TensorFlowNode {
         }
         return result;
     }
-
-    get documentation() {
-        var graphMetadata = this._graph.metadata;
-        if (graphMetadata) {
-            return graphMetadata.getOperatorDocumentation(this.operator);       
-        }
-        return null;
-    }
 }
 
 class TensorFlowTensor {
 
-    constructor(tensor, id) {
+    constructor(tensor, id, title) {
         this._tensor = tensor;
         this._id = id;
+        if (title) {
+            this._title = title;
+        }
     }
 
     get id() {
@@ -269,6 +356,10 @@ class TensorFlowTensor {
         return TensorFlowTensor.formatTensorType(this._tensor);
     }
 
+    get title() {
+        return this._title;
+    }
+
     get value() {
         return '?';        
     }
@@ -279,13 +370,41 @@ class TensorFlowTensor {
     }
 }
 
-class TensorFlowGraphMetadata {
+class TensorFlowOperatorMetadata {
+
+    constructor(hostService) {
+        this._map = {};
+        hostService.request('/tf-operator.json', (err, data) => {
+            if (err != null) {
+                // TODO error
+            }
+            else {
+                var items = JSON.parse(data);
+                if (items) {
+                    items.forEach((item) => {
+                        if (item.name && item.schema)
+                        {
+                            var name = item.name;
+                            var schema = item.schema;
+                            this._map[name] = schema;
+                        }
+                    });
+                }
+            }
+        });
+    }
+
+    getSchema(operator) {
+        return this._map[operator];
+    }
+}
+
+class TensorFlowGraphOperatorMetadata {
 
     constructor(metaInfoDef) {
-        var self = this;
-        self.schemaMap = {};
+        this._map = {};
         if (metaInfoDef && metaInfoDef.strippedOpList && metaInfoDef.strippedOpList.op) {
-            metaInfoDef.strippedOpList.op.forEach(function (opDef) {
+            metaInfoDef.strippedOpList.op.forEach((opDef) => {
                 var schema = { inputs: [], outputs: [], attributes: [] };
                 opDef.inputArg.forEach(function (inputArg) {
                     schema.inputs.push({ name: inputArg.name, typeStr: inputArg.typeAttr });
@@ -296,13 +415,21 @@ class TensorFlowGraphMetadata {
                 opDef.attr.forEach(function (attr) {
                     schema.attributes.push({ name: attr.name, type: attr.type });
                 });
-                self.schemaMap[opDef.name] = schema;
+                this._map[opDef.name] = schema;
             });
         }
     }
 
+    getSchema(operator) {
+        var schema = TensorFlowModel.operatorMetadata.getSchema(operator);
+        if (!schema) {
+            schema = this._map[operator];
+        }
+        return schema;
+    }
+
     getInputName(operator, index) {
-        var schema = this.schemaMap[operator];
+        var schema = this.getSchema(operator);
         if (schema) {
             var inputs = schema.inputs;
             if (inputs && index < inputs.length) {
@@ -319,7 +446,7 @@ class TensorFlowGraphMetadata {
     }
 
     getOutputName(operator, index) {
-        var schema = this.schemaMap[operator];
+        var schema = this.getSchema(operator);
         if (schema) {
             var outputs = schema.outputs;
             if (outputs && index < outputs.length) {
@@ -336,8 +463,21 @@ class TensorFlowGraphMetadata {
     }
 
     getOperatorDocumentation(operator) {
-        var schema = this.schemaMap[operator];
+        var schema = this.getSchema(operator);
         if (schema) {
+            schema = Object.assign({}, schema);
+            schema.name = operator;
+            schema.summary = marked(schema.summary);
+            schema.description = marked(schema.description);
+            schema.inputs.forEach((input) => {
+                input.description = marked(input.description);
+            });
+            schema.outputs.forEach((output) => {
+                output.description = marked(output.description);
+            });
+            schema.attributes.forEach((attribute) => {
+                attribute.description = marked(attribute.description);
+            });
             var template = Handlebars.compile(operatorTemplate, 'utf-8');
             return template(schema);
         }

+ 20 - 4
src/view-tflite.js

@@ -188,6 +188,26 @@ class TensorFlowLiteNode {
         return this._operator;
     }
 
+    get name() {
+        return null;
+    }
+
+    get primitive() {
+        return null;
+    }
+
+    get constant() {
+        return null;
+    }
+
+    get documentation() {
+        return null;
+    }
+
+    get domain() {
+        return null;
+    }
+
     get inputs() {
         if (!this._inputs) {
             this._inputs = [];
@@ -227,10 +247,6 @@ class TensorFlowLiteNode {
         return this._outputs;
     }
 
-    get properties() {
-        return [];
-    }
-
     get attributes() {
         if (!this._attributes) {
             this._attributes = [];

+ 5 - 10
src/view.css

@@ -138,7 +138,7 @@ button:hover {
     user-select: none;
 }
 
-.sidebar a {
+.sidebar-closebutton {
     padding: 8px 8px 8px 32px;
     text-decoration: none;
     font-size: 25px;
@@ -146,24 +146,19 @@ button:hover {
     opacity: 1.0;
     display: block;
     transition: 0.2s;
-    height: 100%;
     -webkit-user-select: none;
     -moz-user-select: none;
     user-select: none;
-}
-
-.sidebar a:hover {
-    color: #f1f1f1;
-}
-
-.sidebar-closebutton {
     position: absolute;
     top: 0;
     right: 15px;
-    font-size: 36px;
     margin-left: 50px;
 }
 
+.sidebar-closebutton:hover {
+    color: #f1f1f1;
+}
+
 .sidebar-content {
     padding-left: 20px;
     padding-right: 20px;

+ 39 - 29
src/view.js

@@ -11,7 +11,7 @@ document.body.scroll = 'no';
 var navigationButton = document.getElementById('navigation-button');
 if (navigationButton) {
     navigationButton.addEventListener('click', (e) => {
-        showSummary(modelService.activeModel);
+        showModelSummary(modelService.activeModel);
     });
 }
 
@@ -111,30 +111,41 @@ function updateGraph(model) {
     var edgeMap = {};
 
     var initializerMap = {};
-    graph.initializers.forEach(function (initializer) {
+    graph.initializers.forEach((initializer) => {
         var id = initializer.id;
         initializerMap[id] = initializer;
     });
 
-    graph.nodes.forEach(function (node) {
+    graph.nodes.forEach((node) => {
         var formatter = new NodeFormatter();
-        var style = (node.operator != 'Constant' && node.operator != 'Const') ? 'node-item-operator' : 'node-item-constant';
-        formatter.addItem(node.operator, style, null, function() { 
+        var style = node.constant ? 'node-item-constant' : 'node-item-operator';
+        var primitive = node.primitive;
+        formatter.addItem(primitive ? primitive : node.operator, style, node.name, function() { 
             showNodeOperatorDocumentation(node);
         });
 
-        node.inputs.forEach(function (input)
-        {
+        var hasInitializerInputs = false;
+        node.inputs.forEach((input) => {
+            if (initializerMap[input.id]) {
+                hasInitializerInputs = true;
+            }
+        });
+
+        node.inputs.forEach((input) => {
             var inputId = input.id;
             var initializer = initializerMap[inputId];
             if (initializer) {
-                formatter.addItem(input.name, 'node-item-constant', initializer.type, function() { 
-                    showTensor(model, initializer);
-                });
+                if (!primitive || hasInitializerInputs) {
+                    formatter.addItem(input.name, 'node-item-constant', initializer.type, function() { 
+                        showTensor(model, initializer);
+                    });
+                }
             }
             else {
                 // TODO is there a way to infer the type of the6 input?
-                formatter.addItem(input.name, null, input.type, null);
+                if (!primitive || hasInitializerInputs) {
+                    formatter.addItem(input.name, null, input.type, null);
+                }
                 var tuple = edgeMap[inputId];
                 if (!tuple) {
                     tuple = { from: null, to: [] };
@@ -147,7 +158,7 @@ function updateGraph(model) {
             }
         });
 
-        node.outputs.forEach(function (output)
+        node.outputs.forEach((output) =>
         {
             var outputId = output.id;
             var tuple = edgeMap[outputId];
@@ -161,20 +172,11 @@ function updateGraph(model) {
             };
         });
 
-        if (node.properties) {
-            formatter.setPropertyHandler(function() {
-                showNodeProperties(node);
+        if (node.attributes && !primitive) {
+            formatter.setAttributeHandler(() => { 
+                showNodeDetails(node);
             });
-            node.properties.forEach(function (property) {
-                formatter.addProperty(property.name, property.value_short());
-            });
-        }
-
-        if (node.attributes) {
-            formatter.setAttributeHandler(function() { 
-                showNodeAttributes(node);
-            });
-            node.attributes.forEach(function (attribute) {
+            node.attributes.forEach((attribute) => {
                 formatter.addAttribute(attribute.name, attribute.value_short(), attribute.type);
             });
         }
@@ -182,7 +184,7 @@ function updateGraph(model) {
         g.setNode(nodeId++, { label: formatter.format(svg).node(), labelType: 'svg', padding: 0 });
     });
 
-    graph.inputs.forEach(function (input) {
+    graph.inputs.forEach((input) => {
         var tuple = edgeMap[input.id];
         if (!tuple) {
             tuple = { from: null, to: [] };
@@ -198,7 +200,7 @@ function updateGraph(model) {
         g.setNode(nodeId++, { label: formatter.format(svg).node(), class: 'graph-input', labelType: 'svg', padding: 0 } ); 
     });
 
-    graph.outputs.forEach(function (output) {
+    graph.outputs.forEach((output) => {
         var outputId = output.id;
         var outputName = output.name;
         var tuple = edgeMap[outputId];
@@ -216,7 +218,7 @@ function updateGraph(model) {
         g.setNode(nodeId++, { label: formatter.format(svg).node(), labelType: 'svg', padding: 0 } ); 
     });
 
-    Object.keys(edgeMap).forEach(function (edge) {
+    Object.keys(edgeMap).forEach((edge) => {
         var tuple = edgeMap[edge];
         if (tuple.from != null) {
             tuple.to.forEach(function (to) {
@@ -286,7 +288,7 @@ function updateGraph(model) {
     }, 20);
 }
 
-function showSummary(model) {
+function showModelSummary(model) {
     var view = model.format();
     if (view) {
         var template = Handlebars.compile(summaryTemplate, 'utf-8');
@@ -311,6 +313,14 @@ function showNodeOperatorDocumentation(node) {
     }
 }
 
+function showNodeDetails(node) {
+    if (node) {
+        var template = Handlebars.compile(nodeTemplate, 'utf-8');
+        var data = template(node);
+        sidebar.open(data, 'Node Details');
+    }
+}
+
 function showNodeProperties(node) {
     if (node.properties) {
         var template = Handlebars.compile(itemsTemplate, 'utf-8');

+ 6 - 5
tools/onnx-operator-json.py

@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 
 from __future__ import unicode_literals
 
@@ -32,7 +32,7 @@ def generate_json_types(types):
     r = sorted(r)
     return r
 
-def generate_json(schemas, file):
+def generate_json(schemas, json_file):
     json_root = []
     for schema in schemas:
         json_schema = {}
@@ -43,7 +43,7 @@ def generate_json(schemas, file):
         json_schema['since_version'] = schema.since_version
         json_schema['support_level'] = generate_json_support_level_name(schema.support_level)
         if schema.doc:
-            json_schema['doc'] = schema.doc.lstrip();
+            json_schema['description'] = schema.doc.lstrip();
         if schema.inputs:
             json_schema['inputs'] = []
             for input in schema.inputs:
@@ -101,8 +101,9 @@ def generate_json(schemas, file):
                 })
         json_root.append({
             "name": schema.name,
-            "schema": json_schema })
-    with io.open(file, 'w', newline='') as fout:
+            "schema": json_schema 
+        })
+    with io.open(json_file, 'w', newline='') as fout:
         json_root = json.dumps(json_root, sort_keys=True, indent=2)
         for line in json_root.splitlines():
             line = line.rstrip()

+ 3 - 0
tools/tf-generate

@@ -24,3 +24,6 @@ echo "Generate '../src/tf.js'"
 echo "Generate '../src/tflite.js'"
 flatc --js ../third_party/tensorflow/tensorflow/contrib/lite/schema/schema.fbs 
 mv ./schema_generated.js ../src/tflite.js
+
+echo "Generate '../src/tf-operator.json'"
+python tf-operator-json.py

+ 68 - 0
tools/tf-operator-json.py

@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+
+from __future__ import unicode_literals
+
+import json
+import io
+import sys
+
+from tensorflow.core.framework import op_def_pb2
+from google.protobuf import text_format
+
+ops_file = '../third_party/tensorflow/tensorflow/core/ops/ops.pbtxt';
+ops_list = op_def_pb2.OpList()
+
+json_file = '../src/tf-operator.json'
+
+with open(ops_file) as text_file:
+    text = text_file.read()
+    text_format.Merge(text, ops_list)
+
+json_root = []
+
+for schema in ops_list.op:
+    json_schema = {}
+
+    if schema.summary:
+        json_schema['summary'] = schema.summary
+    if schema.description:
+        json_schema['description'] = schema.description
+    if schema.input_arg:
+        json_schema['inputs'] = []
+        for input_arg in schema.input_arg:
+            json_schema['inputs'].append({
+                'name': input_arg.name,
+                'type': input_arg.type,
+                'description': input_arg.description
+            })
+    if schema.output_arg:
+        json_schema['outputs'] = []
+        for output_arg in schema.output_arg:
+            json_schema['outputs'].append({
+                'name': output_arg.name,
+                'type': output_arg.type,
+                'description': output_arg.description
+            })
+    if schema.attr:
+        json_schema['attributes'] = []
+        for attr in schema.attr:
+            json_schema['attributes'].append({
+                'name': attr.name,
+                'type': attr.type,
+                'description': attr.description
+            })
+    json_root.append({
+        'name': schema.name,
+        'schema': json_schema
+    })
+
+with io.open(json_file, 'w', newline='') as fout:
+    json_root = json.dumps(json_root, sort_keys=True, indent=2)
+    for line in json_root.splitlines():
+        line = line.rstrip()
+        if sys.version_info[0] < 3:
+            line = unicode(line)
+        fout.write(line)
+        fout.write('\n')
+
+

部分文件因为文件数量过多而无法显示