Lutz Roeder 1 год назад
Родитель
Сommit
c74f7d2865
5 измененных файлов с 155 добавлено и 162 удалено
  1. 31 36
      source/browser.js
  2. 6 16
      source/electron.mjs
  3. 14 4
      source/grapher.js
  4. 101 106
      source/view.js
  5. 3 0
      test/worker.js

+ 31 - 36
source/browser.js

@@ -83,7 +83,7 @@ host.BrowserHost = class {
             }
             if (consent) {
                 this.document.body.classList.remove('spinner');
-                await this._message('This app uses cookies to report errors and anonymous usage information.', 'Accept');
+                await this.message('This app uses cookies to report errors and anonymous usage information.', 'Accept');
             }
             this._setCookie('consent', Date.now().toString(), 30);
         };
@@ -145,11 +145,10 @@ host.BrowserHost = class {
         if (this._meta.file) {
             const [url] = this._meta.file;
             if (this._view.accept(url)) {
-                this._openModel(this._url(url), null);
-                if (this._meta.identifier) {
-                    this._document.title = this._meta.identifier;
+                const status = await this._openModel(this._url(url), this._meta.identifier || null);
+                if (status === '') {
+                    return;
                 }
-                return;
             }
         }
         const search = this.window.location.search;
@@ -163,9 +162,8 @@ host.BrowserHost = class {
                 .replace(/^https:\/\/github\.com\/([\w-]*\/[\w-]*)\/raw\/([\w/\-_.]*)$/, 'https://raw.githubusercontent.com/$1/$2')
                 .replace(/^https:\/\/huggingface.co\/(.*)\/blob\/(.*)$/, 'https://huggingface.co/$1/resolve/$2');
             if (this._view.accept(identifier || location)) {
-                const title = await this._openModel(location, identifier);
-                if (title) {
-                    this.document.title = title;
+                const status = await this._openModel(location, identifier);
+                if (status === '') {
                     return;
                 }
             }
@@ -224,10 +222,6 @@ host.BrowserHost = class {
         return 0;
     }
 
-    confirm(message, detail) {
-        return confirm(`${message} ${detail}`);
-    }
-
     async require(id) {
         return import(`${id}.js`);
     }
@@ -440,15 +434,7 @@ host.BrowserHost = class {
             this._view.show('welcome');
             return null;
         }
-        try {
-            await this._view.open(context);
-            return identifier || context.identifier;
-        } catch (err) {
-            if (err) {
-                this._view.error(err, null, 'welcome');
-            }
-            return null;
-        }
+        return await this._openContext(context);
     }
 
     async _open(file, files) {
@@ -456,12 +442,11 @@ host.BrowserHost = class {
         const context = new host.BrowserHost.BrowserFileContext(this, file, files);
         try {
             await context.open();
-            this._telemetry.set('session_engaged', 1);
-            await this._view.open(context);
-            this._view.show(null);
-            this.document.title = files[0].name;
+            await this._openContext(context);
         } catch (error) {
-            this._view.error(error, null, null);
+            if (error) {
+                this._view.error(error, error.name, null);
+            }
         }
     }
 
@@ -485,20 +470,30 @@ host.BrowserHost = class {
             const buffer = encoder.encode(file.content);
             const stream = new base.BinaryStream(buffer);
             const context = new host.BrowserHost.Context(this, '', identifier, stream);
-            this._telemetry.set('session_engaged', 1);
-            try {
-                await this._view.open(context);
-                this.document.title = identifier;
-            } catch (error) {
-                if (error) {
-                    this._view.error(error, error.name, 'welcome');
-                }
-            }
+            await this._openContext(context);
         } catch (error) {
             this._view.error(error, 'Model load request failed.', 'welcome');
         }
     }
 
+    async _openContext(context) {
+        this._telemetry.set('session_engaged', 1);
+        try {
+            const model = await this._view.open(context);
+            if (model) {
+                this.document.title = context.identifier;
+                return '';
+            }
+            this.document.title = '';
+            return 'context-open-failed';
+        } catch (error) {
+            if (error) {
+                this._view.error(error, error.name, null);
+            }
+            return 'context-open-error';
+        }
+    }
+
     _setCookie(name, value, days) {
         this.document.cookie = `${name}=; Max-Age=0`;
         const location = this.window.location;
@@ -554,7 +549,7 @@ host.BrowserHost = class {
         return this.document.getElementById(id);
     }
 
-    _message(message, action) {
+    message(message, action) {
         return new Promise((resolve) => {
             this._element('message-text').innerText = message;
             const button = this._element('message-button');

+ 6 - 16
source/electron.mjs

@@ -92,7 +92,7 @@ host.ElectronHost = class {
                     // continue regardless of error
                 }
                 if (consent) {
-                    await this._message('This app uses cookies to report errors and anonymous usage information.', 'Accept');
+                    await this.message('This app uses cookies to report errors and anonymous usage information.', 'Accept');
                 }
                 this.set('consent', Date.now());
             }
@@ -242,19 +242,7 @@ host.ElectronHost = class {
             buttons: cancel ? ['Report', 'Cancel'] : ['Report']
         };
         return electron.ipcRenderer.sendSync('show-message-box', options);
-        // return await this._message(message + ': ' + detail, 'Report');
-    }
-
-    confirm(message, detail) {
-        const result = electron.ipcRenderer.sendSync('show-message-box', {
-            type: 'question',
-            message,
-            detail,
-            buttons: ['Yes', 'No'],
-            defaultId: 0,
-            cancelId: 1
-        });
-        return result === 0;
+        // return await this.message(message + ': ' + detail, 'Report');
     }
 
     async require(id) {
@@ -446,13 +434,15 @@ host.ElectronHost = class {
                 if (model) {
                     options.path = path;
                     this._title(location.label);
+                } else {
+                    options.path = path;
+                    this._title('');
                 }
                 this._update(options);
             } catch (error) {
                 const options = { ...this._view.options };
                 if (error) {
                     await this._view.error(error, null, null);
-                    options.path = null;
                 }
                 this._update(options);
             }
@@ -572,7 +562,7 @@ host.ElectronHost = class {
         this._document.body.setAttribute('class', 'welcome message');
     }
 
-    _message(message, action) {
+    message(message, action) {
         return new Promise((resolve) => {
             this._element('message-text').innerText = message;
             const button = this._element('message-button');

+ 14 - 4
source/grapher.js

@@ -30,10 +30,10 @@ grapher.Graph = class {
 
     setEdge(edge) {
         if (!this._nodes.has(edge.v)) {
-            throw new grapher.Error(`Invalid edge '${JSON.stringify(edge.v)}'.`);
+            throw new Error(`Invalid edge '${JSON.stringify(edge.v)}'.`);
         }
         if (!this._nodes.has(edge.w)) {
-            throw new grapher.Error(`Invalid edge '${JSON.stringify(edge.w)}'.`);
+            throw new Error(`Invalid edge '${JSON.stringify(edge.w)}'.`);
         }
         const key = `${edge.v}:${edge.w}`;
         if (!this._edges.has(key)) {
@@ -224,21 +224,31 @@ grapher.Graph = class {
         };
         if (host && host.worker) {
             return new Promise((resolve) => {
+                let timeout = -1;
                 const worker = host.worker('./worker');
                 worker.addEventListener('message', (e) => {
                     const message = e.data;
                     worker.terminate();
                     update(message.nodes, message.edges);
-                    resolve();
+                    if (timeout >= 0) {
+                        clearTimeout(timeout);
+                        host.message('');
+                    }
+                    resolve('');
                 });
                 const message = { type: 'dagre.layout', nodes, edges, layout };
                 worker.postMessage(message);
+                timeout = setTimeout(async () => {
+                    await host.message('This large graph layout might take a very long time to complete.', 'Cancel');
+                    worker.terminate();
+                    resolve('graph-layout-cancelled');
+                }, 2500);
             });
         }
         const dagre = await import('./dagre.js');
         dagre.layout(nodes, edges, layout, {});
         update(nodes, edges);
-        return Promise.resolve();
+        return Promise.resolve('');
     }
 
     update() {

+ 101 - 106
source/view.js

@@ -619,7 +619,7 @@ view.View = class {
         if (button === 0 && (url || this._host.type === 'Electron')) {
             this._host.openURL(url || `${this._host.environment('repository')}/issues`);
         }
-        this.show(screen ? screen : 'welcome');
+        this.show(screen);
     }
 
     accept(file, size) {
@@ -693,26 +693,16 @@ view.View = class {
     }
 
     async _updateGraph(model, stack) {
-        await this._timeout(100);
-        const graph = Array.isArray(stack) && stack.length > 0 ? stack[0].graph : null;
-        if (graph && (this._stack.length === 0 || graph !== this._stack[0].graph)) {
-            const nodes = graph.nodes;
-            if (nodes.length > 2048) {
-                if (!this._host.confirm('Large model detected.', 'This graph contains a large number of nodes and might take a long time to render. Do you want to continue?')) {
-                    this._host.event('graph_view', {
-                        graph_node_count: nodes.length,
-                        graph_skip: 1 }
-                    );
-                    this.show(null);
-                    return null;
-                }
-            }
-        }
         const update = async (model, stack) => {
             this._model = model;
             this._stack = stack;
-            await this.renderGraph(this._model, this.activeGraph, this.activeSignature, this._options);
-            if (this._page !== 'default') {
+            const status = await this.renderGraph(this._model, this.activeGraph, this.activeSignature, this._options);
+            if (status !== '') {
+                this._model = null;
+                this._stack = [];
+                this._activeGraph = null;
+                this.show(null);
+            } else if (this._page !== 'default') {
                 this.show('default');
             }
             const path = this._element('toolbar-path');
@@ -720,43 +710,45 @@ view.View = class {
             while (path.children.length > 1) {
                 path.removeChild(path.lastElementChild);
             }
-            if (this._stack.length <= 1) {
-                back.style.opacity = 0;
-            } else {
-                back.style.opacity = 1;
-                const last = this._stack.length - 2;
-                const count = Math.min(2, last);
-                if (count < last) {
-                    const element = this._host.document.createElement('button');
-                    element.setAttribute('class', 'toolbar-path-name-button');
-                    element.innerHTML = '&hellip;';
-                    path.appendChild(element);
-                }
-                for (let i = count; i >= 0; i--) {
-                    const graph = this._stack[i].graph;
-                    const element = this._host.document.createElement('button');
-                    element.setAttribute('class', 'toolbar-path-name-button');
-                    element.addEventListener('click', () => {
-                        if (i > 0) {
-                            this._stack = this._stack.slice(i);
-                            this._updateGraph(this._model, this._stack);
+            if (status === '') {
+                if (this._stack.length <= 1) {
+                    back.style.opacity = 0;
+                } else {
+                    back.style.opacity = 1;
+                    const last = this._stack.length - 2;
+                    const count = Math.min(2, last);
+                    if (count < last) {
+                        const element = this._host.document.createElement('button');
+                        element.setAttribute('class', 'toolbar-path-name-button');
+                        element.innerHTML = '&hellip;';
+                        path.appendChild(element);
+                    }
+                    for (let i = count; i >= 0; i--) {
+                        const graph = this._stack[i].graph;
+                        const element = this._host.document.createElement('button');
+                        element.setAttribute('class', 'toolbar-path-name-button');
+                        element.addEventListener('click', () => {
+                            if (i > 0) {
+                                this._stack = this._stack.slice(i);
+                                this._updateGraph(this._model, this._stack);
+                            }
+                            this.showDefinition(this._stack[0]);
+                        });
+                        let name = '';
+                        if (graph && graph.identifier) {
+                            name = graph.identifier;
+                        } else if (graph && graph.name) {
+                            name = graph.name;
                         }
-                        this.showDefinition(this._stack[0]);
-                    });
-                    let name = '';
-                    if (graph && graph.identifier) {
-                        name = graph.identifier;
-                    } else if (graph && graph.name) {
-                        name = graph.name;
-                    }
-                    if (name.length > 24) {
-                        element.setAttribute('title', name);
-                        element.innerHTML = `&hellip;${name.substring(name.length - 24, name.length)}`;
-                    } else {
-                        element.removeAttribute('title');
-                        element.innerHTML = name;
+                        if (name.length > 24) {
+                            element.setAttribute('title', name);
+                            element.innerHTML = `&hellip;${name.substring(name.length - 24, name.length)}`;
+                        } else {
+                            element.removeAttribute('title');
+                            element.innerHTML = name;
+                        }
+                        path.appendChild(element);
                     }
-                    path.appendChild(element);
                 }
             }
         };
@@ -797,7 +789,7 @@ view.View = class {
             canvas.removeChild(canvas.lastChild);
         }
         if (!graph) {
-            return;
+            return '';
         }
         this._zoom = 1;
         const groups = graph.groups || false;
@@ -833,59 +825,62 @@ view.View = class {
         await this._timeout(20);
         viewGraph.measure();
         // await viewGraph.layout(null);
-        await viewGraph.layout(this._host);
-        viewGraph.update();
-        const elements = Array.from(canvas.getElementsByClassName('graph-input') || []);
-        if (elements.length === 0) {
-            const nodeElements = Array.from(canvas.getElementsByClassName('graph-node') || []);
-            if (nodeElements.length > 0) {
-                elements.push(nodeElements[0]);
-            }
-        }
-        const size = canvas.getBBox();
-        const margin = 100;
-        const width = Math.ceil(margin + size.width + margin);
-        const height = Math.ceil(margin + size.height + margin);
-        origin.setAttribute('transform', `translate(${margin}, ${margin}) scale(1)`);
-        background.setAttribute('width', width);
-        background.setAttribute('height', height);
-        this._width = width;
-        this._height = height;
-        delete this._scrollLeft;
-        delete this._scrollRight;
-        canvas.setAttribute('viewBox', `0 0 ${width} ${height}`);
-        canvas.setAttribute('width', width);
-        canvas.setAttribute('height', height);
-        this._zoom = 1;
-        this._updateZoom(this._zoom);
-        const container = this._element('graph');
-        if (elements && elements.length > 0) {
-            // Center view based on input elements
-            const xs = [];
-            const ys = [];
-            for (let i = 0; i < elements.length; i++) {
-                const element = elements[i];
-                const rect = element.getBoundingClientRect();
-                xs.push(rect.left + (rect.width / 2));
-                ys.push(rect.top + (rect.height / 2));
-            }
-            let [x] = xs;
-            const [y] = ys;
-            if (ys.every((y) => y === ys[0])) {
-                x = xs.reduce((a, b) => a + b, 0) / xs.length;
-            }
-            const graphRect = container.getBoundingClientRect();
-            const left = (container.scrollLeft + x - graphRect.left) - (graphRect.width / 2);
-            const top = (container.scrollTop + y - graphRect.top) - (graphRect.height / 2);
-            container.scrollTo({ left, top, behavior: 'auto' });
-        } else {
-            const canvasRect = canvas.getBoundingClientRect();
-            const graphRect = container.getBoundingClientRect();
-            const left = (container.scrollLeft + (canvasRect.width / 2) - graphRect.left) - (graphRect.width / 2);
-            const top = (container.scrollTop + (canvasRect.height / 2) - graphRect.top) - (graphRect.height / 2);
-            container.scrollTo({ left, top, behavior: 'auto' });
+        const status = await viewGraph.layout(this._host);
+        if (status === '') {
+            viewGraph.update();
+            const elements = Array.from(canvas.getElementsByClassName('graph-input') || []);
+            if (elements.length === 0) {
+                const nodeElements = Array.from(canvas.getElementsByClassName('graph-node') || []);
+                if (nodeElements.length > 0) {
+                    elements.push(nodeElements[0]);
+                }
+            }
+            const size = canvas.getBBox();
+            const margin = 100;
+            const width = Math.ceil(margin + size.width + margin);
+            const height = Math.ceil(margin + size.height + margin);
+            origin.setAttribute('transform', `translate(${margin}, ${margin}) scale(1)`);
+            background.setAttribute('width', width);
+            background.setAttribute('height', height);
+            this._width = width;
+            this._height = height;
+            delete this._scrollLeft;
+            delete this._scrollRight;
+            canvas.setAttribute('viewBox', `0 0 ${width} ${height}`);
+            canvas.setAttribute('width', width);
+            canvas.setAttribute('height', height);
+            this._zoom = 1;
+            this._updateZoom(this._zoom);
+            const container = this._element('graph');
+            if (elements && elements.length > 0) {
+                // Center view based on input elements
+                const xs = [];
+                const ys = [];
+                for (let i = 0; i < elements.length; i++) {
+                    const element = elements[i];
+                    const rect = element.getBoundingClientRect();
+                    xs.push(rect.left + (rect.width / 2));
+                    ys.push(rect.top + (rect.height / 2));
+                }
+                let [x] = xs;
+                const [y] = ys;
+                if (ys.every((y) => y === ys[0])) {
+                    x = xs.reduce((a, b) => a + b, 0) / xs.length;
+                }
+                const graphRect = container.getBoundingClientRect();
+                const left = (container.scrollLeft + x - graphRect.left) - (graphRect.width / 2);
+                const top = (container.scrollTop + y - graphRect.top) - (graphRect.height / 2);
+                container.scrollTo({ left, top, behavior: 'auto' });
+            } else {
+                const canvasRect = canvas.getBoundingClientRect();
+                const graphRect = container.getBoundingClientRect();
+                const left = (container.scrollLeft + (canvasRect.width / 2) - graphRect.left) - (graphRect.width / 2);
+                const top = (container.scrollTop + (canvasRect.height / 2) - graphRect.top) - (graphRect.height / 2);
+                container.scrollTo({ left, top, behavior: 'auto' });
+            }
+            this._graph = viewGraph;
         }
-        this._graph = viewGraph;
+        return status;
     }
 
     applyStyleSheet(element, name) {

+ 3 - 0
test/worker.js

@@ -101,6 +101,9 @@ host.TestHost = class {
     exception(error /*, fatal */) {
         this.errors.push(error);
     }
+
+    message() {
+    }
 };
 
 host.TestHost.Context = class {