Tool HTML Completed SugarMelt - Twine/SugarCube editing/cheat tool

b12moguj

Member
May 2, 2017
254
197
285
SugarMelt Tag not appearing in chrome after F12. It works on Firefox. Using Bazzite.
 

Swen21

New Member
Nov 5, 2018
4
0
40
{
"manifest_version" 3,
"name": "SugarMelt",
"version": "3.0.3.1",
"description": "Allows you to change font/style; also adds cheats",
"author": "Lure.of.Chaos@gmail.com",
"homepage_url": " ",
"icons": {
"16": "icons/16.png",
"48": "icons/48.png",
"128": "icons/128.png"
},
"permissions": [
"storage",
"activeTab"
],
"content_scripts": [
{
"matches": [
"<all_urls>",
"file://*/*"
]
}
],
"host_permissions": [
"*://*/*",
"file://*/*",
"file:///"
],
"devtools_page": "devtools.html",
"browser_specific_settings": {
"gecko": {
"id": "SugarMelt@lure0xaos"
}
}

}
VM809:2 Uncaught SyntaxError: Unexpected number
SugarCube.State.active.variables
{lang: 'en', debug: false, musicOn: true, musicVolume: 50, videoMuted: true, …}
(function () {

// Because sugarcube freezes Engine, I can't really hook into stuff like `show` calls, sadly.
// And replacing SugarCube.Engine would not yield the result, since it's also scoped locally.
// Two methods are used: Per-frame fixup and passage events. Seem to work reasonably well.

class VarWatch {

/**
* Param path {String}
*/
constructor(path) {
this.isTemp = path.charCodeAt(0) == "_";
this.fullPath = path;
this.name = path.split(".").pop(); // Include [idx] in name
this.path = VarWatch.splitPath(path);
this.lastPathNode = this.path[this.path.length - 1];
this.lastValue = this.value;
this.freeze = false;
this.allowIncrease = false;
this.allowDecrease = false;
this.add();
}

add() {
if (!VarWatch.watch.has(this.fullPath)) {
VarWatch.watch.set(this.fullPath, this);
this.view = new VarView(this);
this.view.add();
}
}

destroy() {
if (VarWatch.watch.get(this.fullPath) === this) {
VarWatch.watch.delete(this.fullPath);
}
this.view.destroy();
this.view = null;
}

/**
* Param path {String}
* Param trim {Boolean}
*/
static splitPath(path, trim = true) {
if (path.startsWith("$") || path.startsWith("_")) path = path.substr(1);
let split = path.replace(/[\[\]]/g, ".").split(".");
if (trim && split[split.length - 1] == "") split.pop();
return split;
}

static splitPathSmart(path, trim = true) {
var isTemp = path.startsWith("_");
if (path.startsWith("$") || path.startsWith("_")) path = path.substr(1);
let split = path.replace(/[\[\]]/g, ".").split(".");
if (trim && split[split.length - 1] == "") split.pop();
return [split, isTemp];
}

static getValueSmart(path, trim, container) {
var [path, temp] = VarWatch.splitPathSmart(path, trim);
return VarWatch.getValue(temp, path, container);
}

/**
* Param temp {Boolean}
* Param path {String[]}
* Param getContainer {Boolean}
*/
static getValue(temp, path, getContainer = true) {
let root = temp ? SugarCube.State.temporary : getvars();
let i = 0, l = getContainer ? path.length - 1 : path.length;
while (i < l) {
root = root[path[i++]];
if (root === undefined) return VarWatch.NULL_PATH;
}
return root;
}

static save() {
return Array.from(VarWatch.watch.keys());
}

static restore(r) {
for (let p of r) new VarWatch(p);
}

get value() {
return VarWatch.getValue(this.isTemp, this.path, false);
}

set value(v) {
let root = VarWatch.getValue(this.isTemp, this.path, true);
root[this.lastPathNode] = v;
}

change(v) {
var old = this.value;
if (old instanceof Date) {
v = v.split(" ");
var v2 = v[1].split("/");
var d = Date.parse(`${v2[2]}/${v2[1]}/${v2[0]} ${v[0]}Z`);
if (!isNaN(d)) old.setTime(d);
} else {
this.value = v;
this.lastValue = v;
}
}

limitVal(val) {
if (this.allowIncrease) {
return val < this.lastValue ? this.lastValue : val;
} else if (this.allowDecrease) {
return val > this.lastValue ? this.lastValue : val;
} else {
return this.lastValue;
}
}

update() {
if (this.freeze) {
let val = this.value;
if (val instanceof Date) {
val.setTime(this.lastValue = this.limitVal(val.getTime()));
} else if (val != this.lastValue) {
if (typeof(val) == "number" || typeof(val) == "bigint") {
this.value = this.lastValue = this.limitVal(val);
} else {
this.value = this.lastValue;
}
}
} else {
var val = this.value;
if (val instanceof Date) {
this.lastValue = val.getTime();
} else {
this.lastValue = val;
}
}
if (this.view) this.view.update();
}

}
/** @type {Map<String, VarWatch>} */
VarWatch.watch = new Map();
VarWatch.NULL_PATH = { error:"Path does not exists" };
VarWatch.frequency = 10; // Every 10th frame.

// Variable type, in order to display proper data.
const VTString = 0;
const VTNumber = 1;
const VTBool = 2;
const VTOther = 3;
const VTNull = 4;
const VTUndefined = 5;
const VTFunction = 6;
const VTDate = 7;

const storage_key = SugarCube.Config.saves.id + ".varwatch";

class VarView {

/**
* Param watch {VarWatch}
*/
constructor(watch) {
this.watch = watch;
this.lastType = VTNull;
}

add() {
if (!this.view) this.update();
else {
this.view.appendTo(watcherRoot.find("#debug-bar-watch"));
}
}

destroy() {
if (this.view) {
this.view.remove();
this.view = null;
}
}

build(type, val) {
this.destroy();
let content = "";
this.viewType = type;
switch (type) {
case VTUndefined:
content = "<span style='color: red;'>Undefined</span>";
break;
case VTNull:
content = "<span style='color: red;'>Null</span>";
break;
case VTBool:
content = "<input type='checkbox'>";
break;
case VTNumber:
content = "<input type='number'>";
break;
case VTOther:
content = "<span style='color: #fdffb2' class='watch-value'></span>";
break;
case VTFunction:
content = "<span style='color: red;'>Function</span>";
break;
case VTString:
content = "<input>";
case VTDate:
content = "<input>";
break;
default:
content = "<span style='color:red'>Unknown type</span>";
}
this.view = $(`
<tr>
<td class="watch-controls">
<label><input type="checkbox" class="watch-freeze connect-right"><i class='fa' title="${this.watch.name}"></i></label>
<label><input type="checkbox" class="watch-freeze-dir connect-left"><i class='fa' title="Toggle allowed change direction increase/decrease"></i></label>
</td>
<td>
<span title=${this.watch.fullPath}>${this.watch.name}</span>
</td>
<td class='watch-edit'>
${content}
<button class="watch-delete"></button>
</td>
</tr>`);
let self = this;
const DIR_BOTH = "";
const DIR_DOWN = "";
const DIR_UP = "";
this.view.find(".watch-freeze").change( (e) => self.watch.freeze = e.currentTarget.checked );
this.view.find(".watch-freeze-dir").change( function(e) {
if (self.watch.allowDecrease) {
self.watch.allowDecrease = false;
e.currentTarget.checked = false;
e.currentTarget.nextElementSibling.textContent = DIR_BOTH;
} else if (self.watch.allowIncrease) {
self.watch.allowIncrease = false;
self.watch.allowDecrease = true;
e.currentTarget.checked = true;
e.currentTarget.nextElementSibling.textContent = DIR_DOWN;
} else {
self.watch.allowIncrease = true;
e.currentTarget.checked = true;
e.currentTarget.nextElementSibling.textContent = DIR_UP;
}
} );
this.view.find(".watch-delete").click( () => self.watch.destroy() );
this.view.find(".watch-edit input")
.change( function(e) {
if (self.viewType == VTBool) self.watch.change(e.currentTarget.checked);
else if (self.viewType == VTNumber) self.watch.change(parseFloat($(e.currentTarget).val()));
else self.watch.change($(e.currentTarget).val());
})
.on('wheel', function(e) {
if (self.viewType == VTNumber) {
var delta = e.originalEvent.deltaY > 0 ? -1 : 1;
self.watch.change(self.watch.value + delta);
e.originalEvent.preventDefault();
}
});
this.view
.on("mouseover", function(e) {
if (watcherRoot[0].classList.contains("cheat-hide")) {
self.showShadow();
}
})
.on('mouseout', function(e) {
var shadow = watcherRoot.find("#cheat-bar-value-shadow")[0];
if (shadow._view === self) {
shadow.style.left = "100%";
}
});
this.add();
this.assign(val);
}

showShadow() {
var shadow = watcherRoot.find("#cheat-bar-value-shadow").empty()[0];
shadow._view = this;
var vals = this.view.find("td");
shadow.appendChild(vals[1].cloneNode(true));
shadow.appendChild(vals[2].cloneNode(true));
var watch = watcherRoot.find("#debug-bar-watch")[0];
var settings = watcherRoot.find("#cheat-bar-settings")[0];
shadow.style.bottom = "calc(100% + " + (-watch.offsetTop) + "px)";
shadow.style.left = (-shadow.offsetWidth) + "px";
}

stringifyOther(val) {
if (val instanceof Date) {
return "Date { " + val.toLocaleString() + "}";
} else if (val instanceof RegExp) {
return "RegExp " + val.toString();
}
var v = JSON.stringify(val);
if (v.length > 100) {
return v.substr(0, 100) + " [..." + (v.length - 100) + " more omitted]";
}
return v;
// if (e instanceof Array || e instanceof Set) {
// let v = e instanceof Array ? e : Array.from(e);
// for (let i = 0, l = a.length; i < l; i++) {
// arr.push(v.hasOwnProperty(i) ? this.stringifyOther(v) : "<empty>");
// }
// return Object.keys(v).forEach()
// }
}

assign(val) {
let disp = val;
if (this.viewType == VTOther) {
disp = this.stringifyOther(disp);
}
if (disp == this.lastDisp) return;
this.lastDisp = disp;
switch (this.viewType) {
case VTBool:
this.view.find(".watch-edit input").prop("checked", disp);
break;
case VTDate:
/** @type {Date} */
var date = val;
this.view.find(".watch-edit input").val(
date.getUTCHours() + ":" + date.getUTCMinutes() + ":" + date.getUTCSeconds() + " " +
date.getUTCDate() + "/" + (date.getUTCMonth()+1) + "/" + date.getUTCFullYear());
break;
default:
this.view.find(".watch-edit input").val(disp);
this.view.find(".watch-value").text(disp);
}
}

getType(val) {
if (val === undefined) return VTUndefined;
if (val === null) return this.lastType;
switch (typeof(val)) {
case "bigint": case "number":
return this.lastType = VTNumber;
case "boolean":
return this.lastType = VTBool;
case "function":
return this.lastType = VTFunction;
case "string":
return this.lastType = VTString;
case "undefined":
return this.lastType;
case "object":
if (val instanceof Date) {
return this.lastType = VTDate;
}
return this.lastType = VTOther;
case "symbol":
return VTUndefined;
//throw new Error("Symbol type not supported");
}
}

static detectType(val, tnull = 4) {
if (val === undefined) return VTUndefined;
if (val === null) return tnull;
switch (typeof(val)) {
case "bigint": case "number":
return VTNumber;
case "boolean":
return VTBool;
case "function":
return VTFunction;
case "string":
return VTString;
case "undefined":
return tnull;
case "object":
if (val instanceof Date) {
return VTDate;
}
return VTOther;
case "symbol":
return VTUndefined;
//throw new Error("Symbol type not supported");
}
}

update() {
let val = this.watch.value;
let type = this.getType(val);
if (type !== this.viewType) {
this.build(type, val);
} else {
// TODO: Only when input not focused
this.assign(val);
}
}

}

// Primary view html
$(".cheat-bar,#debug-bar-hint").remove();
// TODO: Custom CSS as to not be reliant on specific game css shenanigans.
let watcherRoot = $(`
<div class="cheat-bar" style="right: 0px">
<div id="cheat-bar-value-shadow"></div>
<div id="debug-bar-watch" class="flex-table"></div>
<div>
<button id="debug-bar-watch-toggle" tabindex="0" title="Show watch bar">Watch</button>
<button id="debug-bar-watch-save" tabindex="0" title="Save table to local storage">Save</button>
<label>
<span>Add</span>
<span id="cheat-bar-watch-input-container">
<input id="debug-bar-watch-input" type="text" tabindex="0" autocomplete="off">
<div id="cheat-bar-completion" class="flex-table selectable"></div>
</span>
</label>
<button id="debug-bar-watch-add" tabindex="0" title="Add"></button>
<button id="cheat-bar-settings-toggle" tabindex="0" title="Settings"><i class='fa'></i></button>
</div>
<div id="cheat-bar-settings" hidden>
<button id="cheat-bar-copy-share" title="Copy current table into clipboard">Copy <i class='fa'></i></button>
<button id="cheat-bar-paste-share" title="Load the table from clipboard">Paste <i class='fa'></i></button>
<button id="cheat-bar-clear-table" title="Remove all variables from the table">Clear <i class='fa'></i></button>
<button id="cheat-bar-load-table" title="Load table stored in local storage">Load <i class='fa'></i></button>
</div>
<button id="debug-bar-toggle" tabindex="0" title="Toggle bar"></button>
</div>
<div id="debug-bar-hint"></div>
`).appendTo("body");
// Some games bind hotkeys to navigation - we don't want to trigger those when our bar is focused.
function cancelTraversal(e) {
e.stopPropagation();
}
watcherRoot[0].addEventListener("click", cancelTraversal);
watcherRoot[0].addEventListener("keyup", cancelTraversal);
watcherRoot[0].addEventListener("keydown", cancelTraversal);
watcherRoot[0].addEventListener("textinput", cancelTraversal);
watcherRoot[0].addEventListener("input", cancelTraversal);
// Toggle the bar
watcherRoot.find("#debug-bar-toggle").click( function() {
var b = watcherRoot[0];
if (b.classList.toggle("cheat-hide")) {
b.style.right = (-b.offsetWidth) + "px";
} else {
b.style.right = "0px";
}
});
// Save current state
function getTableString() {
return JSON.stringify(VarWatch.save());
}
watcherRoot.find("#debug-bar-watch-save").click( function() {
localStorage.setItem(storage_key, getTableString());
});
// Watch bar toggle
watcherRoot.find("#debug-bar-watch-toggle").click(function() {
var prop = watcherRoot.find("#debug-bar-watch");
prop.attr("hidden") ? prop.removeAttr("hidden") : prop.attr("hidden", "hidden");
});
watcherRoot.find("#cheat-bar-settings-toggle").click(function() {
var prop = watcherRoot.find("#cheat-bar-settings");
prop.attr("hidden") ? prop.removeAttr("hidden") : prop.attr("hidden", "hidden");
});
// Settings
watcherRoot.find("#cheat-bar-copy-share").click(function() {
navigator.clipboard.writeText(getTableString());
});
watcherRoot.find("#cheat-bar-paste-share").click(async function() {
if ( (await navigator.permissions.query({ name: "clipboard-read" })).state == "granted") {
VarWatch.restore(JSON.parse(await navigator.clipboard.readText()));
} else {
var p = prompt("Please paste the JSON data here:");
if (p) VarWatch.restore(JSON.parse(p));
}
});
watcherRoot.find("#cheat-bar-clear-table").click(function() {
for (w of Array.from(VarWatch.watch.values())) w.destroy();
});
watcherRoot.find("#cheat-bar-load-table").click(function() {
if (localStorage.getItem(storage_key))
VarWatch.restore(JSON.parse(localStorage.getItem(storage_key)));
});

// The watch bar input
let completionIndex = 0;
watcherRoot.find("#debug-bar-watch-input")
.on('input', () => buildCompletion() )
.on('keyup', function(e) {
if (e.originalEvent.code == "Enter") {
watcherRoot.find("#debug-bar-watch-add").click();
} else if (e.originalEvent.code == "Tab") {
e.originalEvent.preventDefault();
}
})
.on('keydown', function(e) {
if (e.originalEvent.code == "Tab") {
var first = watcherRoot.find("#cheat-bar-completion>tr");
first = first[(completionIndex++) % first.length];
if (first) {
watcherRoot.find("#cheat-bar-completion>tr.highlight").removeClass("highlight");
watcherRoot.find("#debug-bar-watch-input").val(first._value);
first.classList.add("highlight");
e.originalEvent.preventDefault();
}
}
})
.on('focus', () => buildCompletion())
.on('blur', (e) => $(e.originalEvent.relatedTarget).parent("#cheat-bar-completion").length == 0 && watcherRoot.find("#cheat-bar-completion").removeClass("show") );
watcherRoot.find("#cheat-bar-completion")
.on('click', function(e) {
var target = e.originalEvent.target;
if (target.tagName == "TR") {
watcherRoot.find("#debug-bar-watch-input").val(target._value).focus();
}
});
// Add button
watcherRoot.find("#debug-bar-watch-add").click(function() {
var inp = watcherRoot.find("#debug-bar-watch-input").val();
if (VarWatch.getValueSmart(inp, false, false) !== undefined) new VarWatch(inp);
watcherRoot.find("#debug-bar-watch-input").val("");
buildCompletion();
});

let lastRoot = null;
function buildCompletion() {
let dl = watcherRoot.find("#cheat-bar-completion").empty()[0];

let inp = watcherRoot.find("#debug-bar-watch-input").val();
let temp = inp.startsWith("_");
completionIndex = 0;
if (inp.length < (temp ? 3 : 2)) return;
inp = VarWatch.splitPath(inp, false);

let val = VarWatch.getValue(temp, inp, true);
lastRoot = val;

if (val) {
let top = inp.pop();
let prefix = watcherRoot.find("#debug-bar-watch-input").val();
if (!prefix.startsWith("$") && !temp) prefix = ("$") + prefix;
prefix = prefix.substr(0, prefix.length - top.length);

for (let k of Object.keys(val)) {
if (top.length == 0 || k.toLowerCase().startsWith(top.toLowerCase())) {
let sval = prefix + k;
let div = document.createElement("tr");
div._value = sval;
div.tabIndex = -1;
switch (VarView.detectType(val[k])) {
case VTString:
div.textContent = sval + ' = "' + val[k].substr(0, 30) + (val[k].length > 30 ? '"...' : '"');
break;
case VTOther:
if (Array.isArray(val[k])) div.textContent = sval + "[" + val[k].length + "]";
else div.textContent = sval + "{}";
break;
case VTNumber:
case VTBool:
case VTNull:
case VTUndefined:
div.textContent = sval + " = " + val[k];
break;
case VTFunction:
div.textContent = sval + "()";
break;
case VTDate:
var date = val[k];
div.textContent = sval + " = "
date.getUTCHours() + ":" + date.getUTCMinutes() + ":" + date.getUTCSeconds() + " " +
date.getUTCDate() + "/" + (date.getUTCMonth()+1) + "/" + date.getUTCFullYear();
break;
}
dl.appendChild(div);
}
}
}

if (dl.children.length > 0) {
if (!dl.classList.contains("show")) dl.classList.add("show");
} else dl.classList.remove("show");
}

// Add refresh hooks
function updateWatch() {
for (let w of VarWatch.watch.values()) w.update();
}
var counter = 0;
function refresh() {
window.__refreshFrame = requestAnimationFrame(refresh);
counter++;
if (counter >= VarWatch.frequency) {
counter = 0;
updateWatch();
}
}

if (window.__destroy) window.__destroy();
window.__destroy = function() {
jQuery(document).off(":passagestart", updateWatch).off(":passageend", updateWatch);
cancelAnimationFrame(window.__refreshFrame);
};

jQuery(document).on(":passagestart", updateWatch).on(":passageend", updateWatch);
requestAnimationFrame(refresh);

// Insert CSS
{
// Font-awesome icons
let fa = document.querySelector("#cheat-icons") || document.createElement("link");
fa.id = "cheat-icons";
fa.href = " ";
fa.rel = "stylesheet";
document.head.appendChild(fa);

let css = document.querySelector("#cheat-css") || document.createElement("style");
css.id = "cheat-css";
document.head.appendChild(css);
css.textContent = `
.cheat-bar {
font-family: "Arial", Times, font-family;
font-size: 1em;
background: #222;
border-left: 1px solid #444;
border-top: 1px solid #444;
bottom: 0;
margin: 0;
max-height: 75%;
padding: .3em;
position: fixed;
right: 0;
z-index: 99990;
}

.cheat-hide #debug-bar-watch {
left: calc(-2.2em - 2px);
}

#debug-bar-watch {
overflow-x: auto;
}

.cheat-bar button {
cursor: pointer;
color: #eee;
background-color: #35a;
border: 1px solid #57c;
line-height: normal;
padding: .2em;
transition-duration: .2s;
user-select: none;
}

#debug-bar-views-toggle, #debug-bar-watch-toggle {
padding: .2em;
margin-right: .1em;
}

#cheat-bar-watch-input-container {
position: relative;
}
#cheat-bar-completion, #cheat-bar-value-shadow {
положение: абсолютное;
z-индекс: 99991;
цвет: #eee;
фоновый цвет: #222;
граница: 1 пиксель сплошная #444;
}
#cheat-bar-completion {
дисплей: нет;
слева: 0px;
внизу: calc(100% + .4em);
отступ справа: .8em;
макс. высота: 50vh;
overflow-y: авто;
}
#cheat-bar-completion tr {
отступ: .4em;
}
#cheat-bar-completion.show {
дисплей: начальный;
}
#cheat-bar-value-shadow {
слева: 100%;
переход: плавность влево 0,2 с, плавность в ширину 0,2 с;
события указателя: нет;
отступ: .2em;
дисплей: гибкий;
выравнивание элементов: базовая линия;
мин. ширина: 300 пикселей;
}
#cheat-bar-value-shadow td:first-child {
отступ справа: 4px;
выравнивание текста: по правому краю;
flex-grow: 1;
}
#cheat-bar-value-shadow button {
дисплей: нет;
}

.cheat-bar-toggle {
цвет: #eee;
фоновый цвет: #222;
граница: 1 пиксель сплошная #444;
высота: расч.(100% + 1px);
слева: -2em;
положение: абсолютное;
верх: -1px;
ширина: 2em;
}

.cheat-bar-toggle::before {
содержимое: "\e838";
}

i.ico, .icon-before::before {
семейство шрифтов: tme-fa-icons;
стиль шрифта: обычный;
начертание шрифта: 400;
вариант шрифта: обычный;
преобразование текста: нет;
высота строки: 1;
говорить: никто
}
.flex-table {
дисплей: гибкий;
flex-direction: столбец;
}
.flex-table tr {
дисплей: гибкий;
ширина: 100%;
выровнять элементы: по центру;
}
.flex-table td {
flex-basis: 0;
flex-grow: 1;
}
.flex-table td.watch-edit {
flex-grow: 5;
дисплей: гибкий;
}
.flex-table .watch-edit входные данные {
ширина: 100%;
}
.flex-table td.watch-controls {
padding-left: 4px !важно;
мин. ширина: 46 пикселей;
по выбору пользователя: нет;
}
.flex-table tr:nth-child(2n) {
цвет фона: rgba(127,127,127,.15);
}
.flex-table.selectable tr:hover {
цвет фона: rgba(108, 108, 108, .47);
}
.watch-controls метка input[type=checkbox] {
дисплей: нет;
}
.watch-controls метка input+i {
фон: #3355aa;
отступ: 4px;
граница: 1 пиксель сплошная #5577cc;
Радиус скругления: 4px;
цвет: белый;
по выбору пользователя: нет;
курсор: указатель;
}
.watch-controls метка input.connect-right+i {
граница справа: нет;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.watch-controls label input.connect-left+i {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
отступ слева: -4px;
}
.watch-controls label input:checked+i {
фон: #228822;
цвет рамки: #4a4;
}
`;
}

// Загрузить сохраненный список наблюдения
если (localStorage.getItem(storage_key))
VarWatch.restore(JSON.parse(localStorage.getItem(storage_key)));

// Помощник консоли: сокращение для переменных с `st`.
функция getvars() {
вернуть SugarCube.State.variables || SugarCube.State.__getUpperName__;
}
если (!window.st) {
Object.defineProperty(window, "st", {
получить() { вернуть getvars(); }
});
}
})();
неопределенный



У когонибуть есть новая версия этого кода для взлома хтмл игр а то перестала нормально работать
 

Pertzaaa

Newbie
Nov 21, 2017
34
121
258
On Opera go to easy setups --> settings --> and then scroll down to find "shortcuts". Choose "configure shortcuts" --> and then find "Developer Tools Console". If you scroll on the Developer Tools' row, you will see 'Type a shortcut' bar. Click that and press F12. And you can call "Developer Tools" with F12. There is your tab sugarmelt. I assume there is similar thing with chrome.