Skip to content

console #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
shiffman opened this issue Jun 16, 2016 · 26 comments
Closed

console #7

shiffman opened this issue Jun 16, 2016 · 26 comments

Comments

@shiffman
Copy link
Member

This github issue is for tracking discussion around how to implement a console for sketches. Users may ultimately still use developer consoles for advanced features, but something simple and basic that provides useful error messages and is interactive. @catarak pointed out that jsbin (http://jsbin.com/) has a console with features almost identical to what we are looking for. The code for that is open source and found here:

https://github.com/jsbin/jsbin

cc @toolness as I know we have had discussions about this as well as it relates to p5.js-widget.

@catarak
Copy link
Member

catarak commented Jun 29, 2016

I found this project the other day (https://github.com/openexchangerates/javascript-sandbox-console), which looks a lot cleaner than the jsbin console, but it hasn't been updated in two years. It works pretty well, except if you define a JS class.

@catarak
Copy link
Member

catarak commented Jun 29, 2016

Also going to link to the discussion about a JS console on the p5.js widget.

@therewasaguy
Copy link
Member

I was talking with @catarak about this and I'd like to work on it. I can at least get the non-interactive console up and running.

I've forked the development branch and can hijack console messages from the iframe contentWindow like this: therewasaguy@229c44b

I am studying up on Redux so let me know if I'm missing something but I think these are the next steps:

  • Send the hijacked console message as an Action
  • Create a Reducer that can turn the original console message into something useful, and update the app's state via the Store
  • Create a Console component that can display the reduced message

@toolness
Copy link
Member

toolness commented Jul 8, 2016

Go for it @therewasaguy! Unfortunately I'm only familiar with React, not Redux, so can't offer any advice on that, but let me know if you need help with anything else!

@therewasaguy
Copy link
Member

therewasaguy commented Jul 12, 2016

Just an update:

I pulled in the upstream changes, which update the iframe content via srcdoc. I played around with it a bit and it seems like the hijackConsole approach I started is not going to work with srcdoc. The iframe restores the console each time srcdoc changes, so we need to override it each time to hijack the console messages. I can still override it, but I can't get the event timing right. Here's what I've tried so far:

  • add a load event listener to iframe, and hijackConsole at that point. This works, but it doesn't catch any console messages that take place before the iframe has finished loading, including during setup().
  • other event listeners like loadstart readystatechange, domcontentloaded don't seem to fire when changing the iframe's srcdoc.
  • Mutation Observer on srcdoc, that was fun and pointless.

I will be able to get this to work by injecting a script directly into the srcdoc that would send console messages to the parent window via postMessage. Hopefully that sounds like a good approach?

@catarak
Copy link
Member

catarak commented Jul 12, 2016

I can also not use srcdoc. I decided to do that rather than add all of the scripts and css files using document.write because @MathuraMG was trying to add a p5 interceptor to add a shadow dom, and couldn't get it to work that way. Though I'm not sure it works with srcdoc. @MathuraMG could you test this?

CodePen actually uses that approach for their console. Here's the script that they inject into each page, which might be helpful, but it's minified:

! function() {
    function n() {
        l(), t()
    }
    function t() {
        window.addEventListener("message", a, !0)
    }
    function e(n) {
        var t;
        try {
            t = {}.toString.call(n)
        } catch (e) {
            t = "[object Object]"
        }
        return t
    }
    function o(n, t) {
        for (var e = n.length; e--;) if (n[e] === t) return !0;
        return !1
    }
    function r(n) {
        return !!(n && "object" == typeof n && "nodeType" in n && 1 === n.nodeType && n.outerHTML)
    }
    function c(n, t) {
        return n.toLowerCase() < t.toLowerCase() ? -1 : 1
    }
    function i(n) {
        if (null === n || "undefined" == typeof n) return 1;
        var t, o = e(n);
        if ("[object Number]" === o || "[object Boolean]" === o || "[object String]" === o) return 1;
        if ("[object Function]" === o || "[object global]" === o) return 2;
        if ("[object Object]" === o) {
            var r = Object.keys(n);
            for (t = 0; t < r.length; t++) {
                var c = n[r[t]];
                if (i = {}.toString.call(c), "[object Function]" === i || "[object Object]" === i || "[object Array]" === i) return 2
            }
            return 1
        }
        if ("[object Array]" === o) {
            for (t = 0; t < n.length; t++) {
                var c = n[t],
                    i = {}.toString.call(c);
                if ("[object Function]" === i || "[object Object]" === i || "[object Array]" === i) return 2
            }
            return 1
        }
        return 2
    }
    function u(n, t, o) {
        var r, i, l = "",
            a = [];
        if (o = o || "", t = t || [], null === n) return "null";
        if ("undefined" == typeof n) return "undefined";
        if (l = e(n), "[object Object]" == l && (l = "Object"), "[object Number]" == l) return "" + n;
        if ("[object Boolean]" == l) return n ? "true" : "false";
        if ("[object Function]" == l) return n.toString().split("\n  ").join("\n" + o);
        if ("[object String]" == l) return '"' + n.replace(/"/g, "'") + '"';
        for (i = 0; i < t.length; i++) if (n === t[i]) return "[circular " + l.slice(1) + ("outerHTML" in n ? " :\n" + n.outerHTML.split("\n").join("\n" + o) : "");
        if (t.push(n), "[object Array]" == l) {
            for (r = 0; r < n.length; r++) a.push(u(n[r], t));
            return "[" + a.join(", ") + "]"
        }
        if (l.match(/Array/)) return l;
        var f = l + " ",
            s = o + "  ";
        if (o.length / 2 < 2) {
            var b = [];
            try {
                for (r in n) b.push(r)
            } catch (j) {}
            for (b.sort(c), r = 0; r < b.length; r++) try {
                a.push(s + b[r] + ": " + u(n[b[r]], t, s))
            } catch (j) {}
        }
        return a.length ? f + "{\n" + a.join(",\n") + "\n" + o + "}" : f + "{}"
    }
    function l() {
        if (window.console) for (var n = 0; n < f.length; n++)! function() {
            var t = f[n];
            window.console[t] && (window.console[t] = function() {
                for (var n = [].slice.call(arguments), e = [], o = [], c = 0; c < n.length; c++) r(n[c]) ? (o.push(u(n[c].outerHTML)), e.push(1)) : (o.push(u(n[c])), e.push(i(n[c])));
                b.postMessage(["console", {
                    "function": t,
                    arguments: o,
                    complexity: Math.max.apply(null, e)
                }], "*"), this.apply(console, n)
            }.bind(console[t]))
        }()
    }
    function a(n) {
        var t = n.data;
        if ("object" == typeof t && "command" === t.type) {
            try {
                var e = window.eval(t.command)
            } catch (r) {
                return void console.error(r.message)
            }
            if (.30000000000000004 === e) return void console.log("I love JavaScript too.");
            if (o(s, t.command)) return void console.log("Plz no WATS.");
            console.log(e)
        }
    }
    var f = ["log", "error", "warn", "info", "debug", "table", "time", "timeEnd", "count", "clear"],
        s = ["({} + [])", "({} + []);", "({} + [])", "({} + []);", "{} + {}", "{} + {};", "({} + {})", "({} + {});", "[] == []", "[] == [];", "[] == ![]", "[] == ![];", "[] + []", "[] + [];"],
        b = window.parent;
    n()
}();

@catarak
Copy link
Member

catarak commented Jul 12, 2016

Also, getting the postMessage communication would be nice if eventually we want the iframe to render from another domain, i.e. S3. This might be sooner rather than later given that I'm going to add media uploads pretty soon, and with cross origin stuff that will only work if everything is on S3 unless I can come up with some workaround.

@therewasaguy
Copy link
Member

awesome, glad postMessage sounds like the right approach! I'll keep goin with it

@MathuraMG
Copy link
Collaborator

@catarak I tried including the p5Interceptor using srcdoc - it works perfectly!
Code here

@catarak
Copy link
Member

catarak commented Jul 14, 2016

Awesome! As for srcdoc incompatibilities, I'm using srcdoc-polyfill to get it to work on Edge and IE.

@catarak catarak added this to the Sprint 08/17/2016 milestone Aug 11, 2016
@lmccart
Copy link
Member

lmccart commented Aug 26, 2016

hey @therewasaguy are you still working on this? if not I will take a crack at it..

@therewasaguy
Copy link
Member

@lmccart feel free, thanks for checking but I'm sorry to say I haven't made any updates since July

@catarak catarak modified the milestones: Sprint 09/06/2016, Sprint 08/25/2016 Sep 1, 2016
@catarak
Copy link
Member

catarak commented Sep 1, 2016

Just found this library postmate which may or may not be helpful.

@shiffman
Copy link
Member Author

A couple notes on the console. . .these may be irrelevant if there ends up being a larger project to use a completely different engine for the console.

  1. When should the console clear? Right now you have to stop and start the sketch to clear the console.
  2. The above behavior makes sense, however, due to how the sketch re-runs as the user types (see live coding #3) the console displays a ton of extra error messages like so:

screen shot 2016-09-16 at 10 49 17 am

3) There seem to be a lot of extraneous error messages related to the "friendly error stuff" -- specifically the "did you mean to use `exp()` message above. Not sure where is a good place to discuss this? @toolness

@toolness
Copy link
Member

Oh funky, I wonder what in the p5 IDE is triggering that friendly error stuff to display? I could add some kind of preference to force it to not display, but those messages are actually helpful when they're meaningful (in this case they're not, b/c exp doesn't seem to be part of the sketch's code).

@catarak
Copy link
Member

catarak commented Sep 16, 2016

@MathuraMG added an interceptor script to the preview iframe to get the canvas text output to work (#50), which may or may not have to do with triggering the friendly errors.

@shiffman Maybe it makes sense for the console to clear every time the sketch plays/re-plays?

@toolness
Copy link
Member

Oh yeah, I bet it is that interceptor script! I can try taking a look and seeing if I can get the friendly error system to not hate it.

@catarak
Copy link
Member

catarak commented Sep 16, 2016

@toolness that would be awesome 😄

@shinytang6
Copy link
Member

Hi all !
I have been investigating and testing for some time on how to make the interactive console work. And wanna to take this up.
Here is my initial idea, Please let me know if l am wrong:)

Currently, the Console component only displays the infomation hijacked from the window console/ injected script. l think it will make sense to divide the Console into two subcomponents(e.g. ConsoleInput&ConsoleOutput) to realize the goal.

  • ConsoleInput needs another code editor(e.g. codeMirror) to allow users to type in and send the data to an Action. Then a reducer is triggered and save it to the Store.

  • According to the current code struture, a life cycle componentWillReceiveProps can be added to PreviewFrame to detect the changes of the typed message and call a evaluate function

  • After evaluation, the result can be saved to consoleEvents as it is done now.

  • ConsoleOutput display the error/result info to a new line of the code editor.

Also, the current communication mechanism across window(using postMessage) is not good enough in my view. For example, hijackConsole.js had to check the window console every 500ms and the postMessage communication is unidirectional. l think it is necessary to support some promise api to communicate, especially for the interaction between the call function and evaluation function described above.
postmate or jschannel may be perferred!

What's more, can l ask if the feature of autocompletion should be added to the console?

@catarak
Copy link
Member

catarak commented Mar 5, 2018

i think this makes sense! this is a pretty large and open-ended issue and i think it could be help to try to break it down into smaller pieces and to prioritize those pieces a bit. what are essential features of an console that's easy to use for beginners?

@shiffman
Copy link
Member Author

shiffman commented Mar 5, 2018

Thanks for this note @shinytang6! Here are some features that I think would be worthwhile / core.

  • errors shown linking to js file and line number (working)
  • clear console button (working)
  • syntax highlighting
  • nice "folding" for browsing object properties and methods, and array elements
  • repeated console logs show only once
  • lower priority: support for console.table and other console methods.

Really the above is just me picking a few key features of chrome developer tools. Perhaps there are some that I am missing? I might prioritize the above items before interactivity, but in terms of that:

  • ability to type in a variable and and return / display value
  • ability to execute code from the console (and show return values automatically)

Not knowing anything I would guess there are some frameworks / libraries out there already that may have some of these features? So researching if there is an open source third-party tool or library we can repurpose or build on top of would be great. Codemirror is a nice idea.

@shinytang6
Copy link
Member

shinytang6 commented Mar 6, 2018

@shiffman l agree that these small pieces should be added before interactivity:)

I would guess there are some frameworks / libraries out there already that may have some of these features

l have tried to find one before but failed:) Please let me know if anyone has one.

I have read the code of some open source web editor and found that they are all implemented console on their own. l really like this one https://github.com/CompuIves/codesandbox-client (its interaction and autocompletion are cool)
In addition what it is done is to separate the console and error information, l think it's also cool if we implement this feature :)

@catarak
Copy link
Member

catarak commented Mar 6, 2018

agreed—i have also looked for an open source console and haven't found anything promising.

@samdenty
Copy link

console-feed may be worth noting. It's handles the console.log message rendering.

  • Console formatting - style and give your logs color, and makes links clickable
  • DOM nodes - easily inspect & expand HTML elements, with syntax highlighting
  • console.table - view your logs in a table format
  • Other console methods:
    • console.time - view the time in milliseconds it takes to complete events
    • console.assert - assert that a statement is truthy
    • console.count - count how many times something occurs
  • Inbuilt JSON serialization - Objects, Functions & DOM elements can be encoded / decoded to and from JSON

@catarak
Copy link
Member

catarak commented Apr 18, 2018

cool, thanks for the tip! this looks great 🌈

@shiffman
Copy link
Member Author

oh, this is exciting to see!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants