fx-compat: Initial fix for Scaffold

Not working:
- Web tests (hidden browser is broken)
- Test Updated status text
- Various platform menu things (code copied from ZP and will use whichever
  approach we settle on there)
This commit is contained in:
Abe Jellinek 2022-06-08 17:16:50 -05:00
parent 1b324f9bd1
commit e537018e71
11 changed files with 504 additions and 325 deletions

View file

@ -0,0 +1,15 @@
addMessageListener('Scaffold:GetDocument', {
receiveMessage() {
dump('received document request\n')
sendAsyncMessage('Scaffold:Document', {
html: new XMLSerializer().serializeToString(content.document),
url: content.location.href
});
}
});
function onLoad() {
sendAsyncMessage('Scaffold:Load', { url: content.location.href });
}
addEventListener('load', onLoad, true);

View file

@ -24,12 +24,14 @@
*/
var Scaffold_Load = new function() {
this.onLoad = Zotero.Promise.coroutine(function* () {
this.onLoad = async function () {
document.addEventListener('dialogaccept', () => this.accept());
var listitem, translator, listcell, set;
var listbox = document.getElementById("listbox");
listbox.addEventListener('dblclick', () => {
var translatorID = document.getElementById("listbox").selectedItem.getUserData("zotero-id");
var translatorID = document.getElementById("listbox").selectedItem.dataset.zoteroID;
if (!translatorID) return;
this.accept();
window.close();
@ -42,44 +44,48 @@ var Scaffold_Load = new function() {
var url = window.arguments[0].url;
var rootUrl = window.arguments[0].rootUrl
url = Zotero.Proxies.proxyToProper(url);
translators["Matching Translators"] = (yield translatorProvider.getWebTranslatorsForLocation(url, rootUrl))[0];
translators["Web Translators"] = (yield translatorProvider.getAllForType("web"))
translators["Matching Translators"] = (await translatorProvider.getWebTranslatorsForLocation(url, rootUrl))[0];
translators["Web Translators"] = (await translatorProvider.getAllForType("web"))
.sort((a, b) => a.label.localeCompare(b.label));
translators["Import Translators"] = (yield translatorProvider.getAllForType("import"))
translators["Import Translators"] = (await translatorProvider.getAllForType("import"))
.sort((a, b) => a.label.localeCompare(b.label));
translators["Export Translators"] = (yield translatorProvider.getAllForType("export"))
translators["Export Translators"] = (await translatorProvider.getAllForType("export"))
.sort((a, b) => a.label.localeCompare(b.label));
translators["Search Translators"] = (yield translatorProvider.getAllForType("search"))
translators["Search Translators"] = (await translatorProvider.getAllForType("search"))
.sort((a, b) => a.label.localeCompare(b.label));
for (set in translators) {
// Make a separator
listitem = document.createElement("listitem");
listitem = document.createXULElement("richlistitem");
listitem.setAttribute("disabled", true);
listitem.setAttribute("label", set);
// Need to set this to disable up/down keys selecting:
listitem.style.MozUserInput = 'none';
listitem.append(set);
listbox.appendChild(listitem);
for (var j=0; j<translators[set].length; j++) {
var translator = translators[set][j];
listitem = document.createElement("listitem");
// set label for type-to-find functionality. This is not displayed.
listitem.setAttribute("label", translator.label);
listitem = document.createXULElement("richlistitem");
// set search label for type-to-find functionality. This is not displayed.
listitem.searchLabel = translator.label;
// And the ID goes in DOM user data
listitem.setUserData("zotero-id", translator.translatorID, null);
listitem.dataset.zoteroID = translator.translatorID;
listcell = document.createElement("listcell");
listcell.setAttribute("label", translator.label);
listcell = document.createXULElement("hbox");
listcell.setAttribute('flex', '1');
listcell.append(translator.label);
listitem.appendChild(listcell);
listcell = document.createElement("listcell");
listcell.setAttribute("label", translator.creator);
listcell = document.createXULElement("hbox");
listcell.setAttribute('width', '130');
listcell.append(translator.creator);
listitem.appendChild(listcell);
listbox.appendChild(listitem);
}
}
});
};
this.accept = function () {
var translatorID = document.getElementById("listbox").selectedItem.getUserData("zotero-id");
var translatorID = document.getElementById("listbox").selectedItem.dataset.zoteroID;
var translator = window.arguments[0].translatorProvider.get(translatorID);
Zotero.debug(translatorID);

View file

@ -25,24 +25,23 @@
-->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://scaffold/skin/scaffold.css" type="text/css"?>
<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
<!DOCTYPE window SYSTEM "chrome://scaffold/locale/scaffold.dtd">
<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&scaffold.load.title;" width="400" height="330" buttons="cancel,accept"
ondialogaccept="Scaffold_Load.accept()" onload="Scaffold_Load.onLoad()"
<window
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&scaffold.load.title;" width="400" height="330"
onload="Scaffold_Load.onLoad()">
<dialog buttons="cancel,accept"
id="scaffold-load">
<script src="chrome://zotero/content/include.js"/>
<script src="load.js"/>
<listbox id="listbox" flex="1">
<listhead>
<listheader label="&scaffold.load.label.label;"/>
<listheader label="&scaffold.load.creator.label;"/>
</listhead>
<listcols>
<listcol flex="1"/>
<listcol width="130"/>
</listcols>
</listbox>
<listheader>
<treecol flex="1" label="&scaffold.load.label.label;"/>
<treecol width="130" label="&scaffold.load.creator.label;"/>
</listheader>
<richlistbox id="listbox" flex="1"/>
</dialog>
</window>

View file

@ -23,7 +23,9 @@
***** END LICENSE BLOCK *****
*/
Components.utils.import("resource://gre/modules/Services.jsm");
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var { E10SUtils } = ChromeUtils.import("resource://gre/modules/E10SUtils.jsm");
import FilePicker from 'zotero/modules/filePicker';
var Zotero = Components.classes["@zotero.org/Zotero;1"]
@ -53,10 +55,12 @@ function fix2028(str) {
}
var Scaffold = new function () {
var _browser, _frames, _document;
var _browser, _frames = [], _document;
var _translatorsLoadedPromise;
var _translatorProvider = null;
var _lastModifiedTime = 0;
this.browser = () => _browser;
var _editors = {};
@ -85,17 +89,19 @@ var Scaffold = new function () {
_browser = document.getElementById('browser');
_browser.addEventListener("pageshow",
_updateFrames, true);
_updateFrames();
window.messageManager.addMessageListener('Scaffold:Load', ({ data }) => {
document.getElementById("browser-url").value = data.url;
});
window.messageManager.loadFrameScript('chrome://scaffold/content/content.js', true);
let browserUrl = document.getElementById("browser-url");
browserUrl.addEventListener('keypress', function (e) {
if (e.keyCode == e.DOM_VK_RETURN) {
_browser.loadURIWithFlags(
browserUrl.value,
Components.interfaces.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE
);
browserUrl.addEventListener('keydown', function (e) {
if (e.key == 'Enter') {
Zotero.debug('Scaffold: Loading URL in browser: ' + browserUrl.value);
_browser.loadURI(browserUrl.value, {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
});
}
});
@ -107,6 +113,8 @@ var Scaffold = new function () {
if (size) {
this.setFontSize(size);
}
document.getElementById('tabpanels').addEventListener('select', event => Scaffold.handleTabSelect(event));
this.generateTranslatorID();
@ -570,9 +578,8 @@ var Scaffold = new function () {
if (translatorID === undefined) {
var io = {};
io.translatorProvider = _translatorProvider;
io.url = _getDocument()?.location.href || 'about:blank';
io.rootUrl = _browser.contentDocument.location.href;
window.openDialog("chrome://scaffold/content/load.xul",
io.url = io.rootUrl = _browser.currentURI.spec;
window.openDialog("chrome://scaffold/content/load.xhtml",
"_blank", "chrome,modal", io);
translator = io.dataOut;
}
@ -752,8 +759,12 @@ var Scaffold = new function () {
};
this.handleTabSelect = function (event) {
if (event.target.tagName != 'tabpanels') {
return;
}
// Focus editor when switching to tab
var tab = event.target.selectedItem.id.match(/^tab-(.+)$/)[1];
var tab = document.getElementById('tabs').selectedItem.id.match(/^tab-(.+)$/)[1];
switch (tab) {
case 'import':
case 'code':
@ -785,7 +796,7 @@ var Scaffold = new function () {
let editImport = document.getElementById('testing_editImport');
let openURL = document.getElementById('testing_openURL');
if (selected.getUserData('test-type') == 'web') {
if (selected.dataset.testType == 'web') {
editImport.setAttribute('disabled', true);
openURL.removeAttribute('disabled');
}
@ -864,7 +875,7 @@ var Scaffold = new function () {
// Handle generic call run('detect'), run('do')
if (functionToRun == "detect" || functionToRun == "do") {
if (document.getElementById('checkbox-web').checked
&& _browser.contentWindow.location.href != 'about:blank') {
&& _browser.currentURI.spec != 'about:blank') {
functionToRun += 'Web';
}
else if (document.getElementById('checkbox-import').checked
@ -887,7 +898,9 @@ var Scaffold = new function () {
_logOutput(`Running ${functionToRun}`);
let input = _getInput(functionToRun);
let input = await _getInput(functionToRun);
Zotero.debug('got input')
Zotero.debug(input)
if (functionToRun.endsWith('Export')) {
let numItems = Zotero.getActiveZoteroPane().getSelectedItems().length;
@ -991,8 +1004,8 @@ var Scaffold = new function () {
/**
* Test target regular expression against document URL and log the result
*/
this.logTargetRegex = function () {
_logOutput(_testTargetRegex(_getDocument()));
this.logTargetRegex = async function () {
_logOutput(_testTargetRegex(await _getDocument()));
};
/**
@ -1016,7 +1029,7 @@ var Scaffold = new function () {
*/
function _selectItems(obj, itemList) {
var io = { dataIn: itemList, dataOut: null };
window.openDialog("chrome://zotero/content/ingester/selectitems.xul",
window.openDialog("chrome://zotero/content/ingester/selectitems.xhtml",
"_blank", "chrome,modal,centerscreen,resizable=yes", io);
return io.dataOut;
@ -1103,7 +1116,7 @@ var Scaffold = new function () {
+ ":" + Zotero.Utilities.lpad(date.getSeconds(), '0', 2)
+ " " + string.replace(/\n/g, "\n ");
// move to end
output.inputField.scrollTop = output.inputField.scrollHeight;
output.scrollTop = output.scrollHeight;
}
/*
@ -1131,7 +1144,7 @@ var Scaffold = new function () {
/*
* gets appropriate input for the given type/method
*/
function _getInput(typeOrMethod) {
async function _getInput(typeOrMethod) {
typeOrMethod = typeOrMethod.toLowerCase();
if (typeOrMethod.endsWith('web')) {
return _getDocument();
@ -1572,7 +1585,7 @@ var Scaffold = new function () {
return Promise.reject(new Error(`Test of type export cannot be created`));
}
let input = _getInput(type);
let input = await _getInput(type);
if (type == "web") {
let tester = new Zotero_TranslatorTester(
@ -1614,6 +1627,14 @@ var Scaffold = new function () {
* populate tests pane and url options in browser pane
*/
this.populateTests = function () {
function wrapWithHBox(elem, { flex = undefined, width = undefined } = {}) {
let hbox = document.createXULElement('hbox');
hbox.append(elem);
hbox.setAttribute('flex', flex);
hbox.setAttribute('width', width);
return hbox;
}
let tests = _loadTestsFromPane();
let validateTestsBroadcaster = document.getElementById('validate-tests');
if (tests === null) {
@ -1626,7 +1647,7 @@ var Scaffold = new function () {
let browserURL = document.getElementById("browser-url");
let currentURL = browserURL.value;
browserURL.removeAllItems();
// browserURL.removeAllItems();
browserURL.value = currentURL;
let listBox = document.getElementById("testing-listbox");
@ -1634,8 +1655,8 @@ var Scaffold = new function () {
let oldStatuses = {};
for (let i = 0; i < count; i++) {
let item = listBox.getItemAtIndex(i);
let [, statusCell] = item.getElementsByTagName('listcell');
oldStatuses[item.getUserData('test-string')] = statusCell.getAttribute('label');
let [, statusCell] = item.firstElementChild.children;
oldStatuses[item.dataset.testString] = statusCell.getAttribute('value');
}
let testIndex = 0;
@ -1645,40 +1666,46 @@ var Scaffold = new function () {
// try to reuse old rows
let item = testIndex < count
? listBox.getItemAtIndex(testIndex)
: document.createElement('listitem');
: document.createXULElement('richlistitem');
item.innerHTML = ''; // clear children/content if reusing
let input = document.createElement('listcell');
input.setAttribute('label', getTestLabel(test));
item.appendChild(input);
let hbox = document.createXULElement('hbox');
hbox.setAttribute('flex', 1);
hbox.setAttribute('align', 'center');
let status = document.createElement('listcell');
status.setAttribute('label', oldStatuses[testString] || 'Not run');
item.appendChild(status);
let input = document.createXULElement('label');
input.value = getTestLabel(test);
hbox.appendChild(wrapWithHBox(input, { flex: 1 }));
let defer = document.createElement('listcell');
defer.setAttribute('type', 'checkbox');
defer.setAttribute('checked', test.defer);
item.appendChild(defer);
let status = document.createXULElement('label');
status.value = oldStatuses[testString] || 'Not run';
hbox.appendChild(wrapWithHBox(status, { width: 150 }));
item.setUserData('test-string', testString, null);
item.setUserData('test-type', test.type);
let defer = document.createXULElement('checkbox');
defer.checked = test.defer;
defer.disabled = true;
hbox.appendChild(wrapWithHBox(defer));
item.appendChild(hbox);
item.dataset.testString = testString;
item.dataset.testType = test.type;
if (testIndex >= count) {
listBox.appendChild(item);
}
if (test.type == 'web') {
browserURL.appendItem(test.url);
}
// if (test.type == 'web') {
// browserURL.appendItem(test.url);
// }
testIndex++;
}
// remove old rows that we didn't reuse
while (listBox.getItemAtIndex(testIndex)) {
listBox.removeItemAt(testIndex);
listBox.getItemAtIndex(testIndex).remove();
}
};
@ -1703,7 +1730,7 @@ var Scaffold = new function () {
this.editImportFromTest = function () {
var listbox = document.getElementById("testing-listbox");
var item = listbox.selectedItems[0];
var test = JSON.parse(item.getUserData("test-string"));
var test = JSON.parse(item.dataset.testString);
if (test.input === undefined) {
_logOutput("Can't edit input of a non-import/search test.");
}
@ -1726,8 +1753,8 @@ var Scaffold = new function () {
this.copyToClipboard = function () {
var listbox = document.getElementById("testing-listbox");
var item = listbox.selectedItems[0];
var url = item.getElementsByTagName("listcell")[0].getAttribute("label");
var test = JSON.parse(item.getUserData("test-string"));
var url = item.getElementsByTagName("label")[0].getAttribute("value");
var test = JSON.parse(item.dataset.testString);
var urlOrData = (test.input !== undefined) ? test.input : url;
Zotero.Utilities.Internal.copyTextToClipboard(urlOrData);
};
@ -1740,15 +1767,14 @@ var Scaffold = new function () {
this.openURL = function (openExternally) {
var listbox = document.getElementById("testing-listbox");
var item = listbox.selectedItems[0];
var url = item.getElementsByTagName("listcell")[0].getAttribute("label");
var url = item.getElementsByTagName("label")[0].getAttribute("value");
if (openExternally) {
Zotero.launchURL(url);
}
else {
_browser.loadURIWithFlags(
url,
Components.interfaces.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE
);
_browser.loadURI(url, {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
});
_showTab('browser');
}
};
@ -1792,14 +1818,14 @@ var Scaffold = new function () {
if (!items || items.length == 0) return; // No action if nothing selected
var tests = [];
for (let item of items) {
item.getElementsByTagName("listcell")[1].setAttribute("label", "Running");
var test = JSON.parse(item.getUserData("test-string"));
item.getElementsByTagName("label")[1].setAttribute("value", "Running");
var test = JSON.parse(item.dataset.testString);
test["ui-item"] = item;
tests.push(test);
}
this.runTests(tests, (obj, test, status, message) => {
test["ui-item"].getElementsByTagName("listcell")[1].setAttribute("label", message);
test["ui-item"].getElementsByTagName("label")[1].setAttribute("value", message);
});
};
@ -1823,8 +1849,8 @@ var Scaffold = new function () {
var itemIndices = items.map(item => listbox.getIndexOfItem(item));
var tests = [];
for (let item of items) {
item.getElementsByTagName("listcell")[1].setAttribute("label", "Updating");
var test = JSON.parse(item.getUserData("test-string"));
item.getElementsByTagName("label")[1].setAttribute("value", "Updating");
var test = JSON.parse(item.dataset.testString);
tests.push(test);
}
@ -1835,13 +1861,13 @@ var Scaffold = new function () {
// Assume sequential. TODO: handle this properly via test ID of some sort
if (newTest) {
message = "Test updated";
items[testsDone].setUserData("test-string", _stringifyTests(newTest, 1));
items[testsDone].dataset.testString = _stringifyTests(newTest, 1);
tests[testsDone] = newTest;
}
else {
message = "Update failed";
}
items[testsDone].getElementsByTagName("listcell")[1].setAttribute("label", message);
items[testsDone].getElementsByTagName("label")[1].setAttribute("value", message);
testsDone++;
});
@ -2011,77 +2037,19 @@ var Scaffold = new function () {
return guid;
}
/*
* updates list of available frames and show URL of active tab
*/
function _updateFrames() {
var doc = _browser.contentDocument;
// Show URL of active tab
document.getElementById("browser-url").value = doc.location.href;
// No need to run if Scaffold isn't open
var menulist = _document.getElementById("menulist-testFrame");
if (!_document || !menulist) return;
menulist.removeAllItems();
var popup = _document.createElement("menupopup");
menulist.appendChild(popup);
_frames = [];
var frames = doc.getElementsByTagName("frame");
if (frames.length) {
_getFrames(frames, popup);
}
else {
var item = _document.createElement("menuitem");
item.setAttribute("label", "Default");
popup.appendChild(item);
_frames = [doc];
}
menulist.selectedIndex = 0;
}
/*
* recursively searches for frames
*/
function _getFrames(frames, popup) {
for (var i = 0; i < frames.length; i++) {
var frame = frames[i];
if (frame.contentDocument) {
// get a good name
var frameName;
if (frame.title) {
frameName = frame.title;
}
else if (frame.name) {
frameName = frame.name;
}
else {
frameName = frame.contentDocument.location.href;
}
// add frame
var item = _document.createElement("menuitem");
item.setAttribute("label", frameName);
popup.appendChild(item);
_frames.push(frame.contentDocument);
// see if frame has its own frames
var subframes = frame.contentDocument.getElementsByTagName("frame");
if (subframes.length) _getFrames(subframes, popup);
}
}
}
/*
* gets selected frame/document
*/
function _getDocument() {
return _frames[_document.getElementById("menulist-testFrame").selectedIndex];
return new Promise((resolve) => {
window.messageManager.addMessageListener('Scaffold:Document', function onDocument({ data }) {
window.messageManager.removeMessageListener('Scaffold:Document', onDocument);
let doc = new DOMParser().parseFromString(data.html, 'text/html');
doc = Zotero.HTTP.wrapDocument(doc, data.url);
resolve(doc);
});
window.messageManager.broadcastAsyncMessage('Scaffold:GetDocument');
});
}
function _getDocumentURL(doc) {

View file

@ -30,10 +30,6 @@
<?xml-stylesheet href="chrome://zotero-platform-version/content/style.css"?>
<?xml-stylesheet href="chrome://zotero-platform/content/zotero-react-client.css"?>
<?xul-overlay href="chrome://zotero/content/containers/containers.xul"?>
<?xul-overlay href="chrome://zotero/content/standalone/editMenuOverlay.xul"?>
<?xul-overlay href="chrome://zotero-platform/content/standalone/menuOverlay.xul"?>
<!DOCTYPE window [
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> %globalDTD;
<!ENTITY % standaloneDTD SYSTEM "chrome://zotero/locale/standalone.dtd" > %standaloneDTD;
@ -44,15 +40,35 @@
]>
<window id="scaffold" width="800" height="600" minheight="600" persist="screenX screenY width height"
title="Scaffold"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="chrome://zotero/content/include.js"/>
<script src="chrome://zotero/content/xpcom/translate/testTranslators/translatorTester.js"/>
<script src="translators.js"/>
<script src="scaffold.js"/>
title="Scaffold"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
style="display: flex;">
<script src="chrome://global/content/globalOverlay.js"/>
<script src="chrome://global/content/contentAreaUtils.js"/>
<html:link rel="localization" href="zotero/menubar.ftl"/>
<html:link rel="localization" href="zotero/textActions.ftl"/>
<script>
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
Services.scriptloader.loadSubScript("chrome://zotero/content/include.js", this);
Services.scriptloader.loadSubScript("resource://zotero/require.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/xpcom/translate/testTranslators/translatorTester.js", this);
Services.scriptloader.loadSubScript("chrome://scaffold/content/translators.js", this);
Services.scriptloader.loadSubScript("chrome://scaffold/content/scaffold.js", this);
// Mozilla scripts
Services.scriptloader.loadSubScript("chrome://global/content/globalOverlay.js", this);
Services.scriptloader.loadSubScript("chrome://global/content/editMenuOverlay.js", this);
Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", this);
if (Zotero.isMac) {
Services.scriptloader.loadSubScript("chrome://global/content/macWindowMenu.js", this);
}
// Custom elements
Services.scriptloader.loadSubScript("chrome://global/content/customElements.js", this);
</script>
<commandset id="mainCommandSet">
<command id="cmd_quitApplication" oncommand="goQuitApplication(event);"/>
@ -100,10 +116,38 @@
<key modifiers="accel" key="9" oncommand="Scaffold.showTabNumbered(9)"/>
</keyset>
<keyset id="editMenuKeys"/>
<!--
TODO: Customize per platform without ifdef?
<keyset id="editMenuKeys">
<key id="key_undo" data-l10n-id="text-action-undo-shortcut" modifiers="accel" command="cmd_undo"/>
<key id="key_redo"
#ifdef XP_UNIX
data-l10n-id="text-action-undo-shortcut"
modifiers="accel,shift"
#else
data-l10n-id="text-action-redo-shortcut"
modifiers="accel"
#endif
command="cmd_redo"/>
<key id="key_cut" data-l10n-id="text-action-cut-shortcut" modifiers="accel" command="cmd_cut"/>
<key id="key_copy" data-l10n-id="text-action-copy-shortcut" modifiers="accel" command="cmd_copy"/>
<key id="key_paste" data-l10n-id="text-action-paste-shortcut" modifiers="accel" command="cmd_paste"/>
<key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
<key id="key_selectAll" data-l10n-id="text-action-select-all-shortcut" modifiers="accel" command="cmd_selectAll"/>
<key id="key_find" key="&findCmd.key;" modifiers="accel" command="cmd_find"/>
<key id="key_findAgain" key="&findAgainCmd.key;" modifiers="accel" command="cmd_findAgain"/>
<key id="key_findPrevious" key="&findAgainCmd.key;" modifiers="shift,accel" command="cmd_findPrevious"/>
<key id="key_findAgain2" keycode="&findAgainCmd.key2;" command="cmd_findAgain"/>
<key id="key_findPrevious2" keycode="&findAgainCmd.key2;" modifiers="shift" command="cmd_findPrevious"/>
</keyset>
-->
<popupset>
<menupopup id="testing-context-menue">
<menupopup id="testing-context-menu">
<menuitem label="&scaffold.testing.copyToClipboard;" tooltiptext="Copy the URL or data for the current test to the clipboard" oncommand="Scaffold.copyToClipboard()"/>
<menuitem id="testing_editImport" label="&scaffold.testing.edit;" tooltiptext="Edit the input data for the current test" oncommand="Scaffold.editImportFromTest()"/>
<menu id="testing_openURL" label="&scaffold.testing.openUrl;">
@ -139,19 +183,44 @@
</menupopup>
</menu>
<menu id="menu_edit">
<menu id="menu_edit" data-l10n-id="menu-edit">
<menupopup id="menu_EditPopup">
<menuitem id="menu_undo" command="cmd_undo"/>
<menuitem id="menu_redo" command="cmd_redo"/>
<menuitem id="menu_undo"
key="key_undo"
command="cmd_undo" data-l10n-id="text-action-undo"/>
<menuitem id="menu_redo"
key="key_redo"
command="cmd_redo" data-l10n-id="text-action-redo"/>
<menuseparator/>
<menuitem id="menu_cut"/>
<menuitem id="menu_copy"/>
<menuitem id="menu_paste"/>
<menuitem id="menu_delete"/>
<menuseparator class="menu-type-library"/>
<menuitem id="menu_selectAll" class="menu-type-library" command="cmd_selectAll"/>
<menuseparator class="menu-type-library"/>
<menuitem id="menu_find" class="menu-type-library" command="cmd_find"/>
<menuitem id="menu_cut"
key="key_cut"
command="cmd_cut" data-l10n-id="text-action-cut"/>
<menuitem id="menu_copy"
key="key_copy"
command="cmd_copy" data-l10n-id="text-action-copy"/>
<menuitem id="menu_paste"
key="key_paste"
command="cmd_paste" data-l10n-id="text-action-paste"/>
<menuitem id="menu_delete"
key="key_delete"
command="cmd_delete" data-l10n-id="text-action-delete"/>
<menuseparator/>
<menuitem id="menu_selectAll"
key="key_selectAll"
command="cmd_selectAll" data-l10n-id="text-action-select-all"/>
<menuseparator/>
<menuitem id="menu_find" label="&findCmd.label;"
key="key_find" accesskey="&findCmd.accesskey;"
command="cmd_find"/>
<!--
#ifdef XP_UNIX
#ifndef XP_MACOSX
<menuseparator/>
<menuitem id="menu_preferences"
oncommand="Zotero.Utilities.Internal.openPreferences()" data-l10n-id="menu-preferences"/>
#endif
#endif
-->
</menupopup>
</menu>
@ -197,11 +266,87 @@
</menupopup>
</menu>
<!-- ifdef mac -->
<commandset id="macCommandSet"> <!-- was mainCommandSet -->
<command id="minimizeWindow"
label="&minimizeWindow.label;"
oncommand="window.minimize();" />
<command id="zoomWindow"
label="&zoomWindow.label;"
oncommand="zoomWindow();" />
</commandset>
<keyset id="macKeyset"> <!-- was mainKeySet -->
<key id="key_minimizeWindow"
command="minimizeWindow"
key="&minimizeWindow.key;"
modifiers="accel"/>
<!-- These are used to build the Application menu under Cocoa widgets -->
<key id="key_preferencesCmdMac"
key="&preferencesCmdMac.commandkey;"
modifiers="accel"/>
<key id="key_hideThisAppCmdMac"
key="&hideThisAppCmdMac.commandkey;"
modifiers="accel"/>
<key id="key_hideOtherAppsCmdMac"
key="&hideOtherAppsCmdMac.commandkey;"
modifiers="accel,alt"/>
<key id="key_quitApplication"
key="&quitApplicationCmdMac.key;"
command="cmd_quitApplication"
modifiers="accel"/>
</keyset>
<!-- nsMenuBarX hides these and uses them to build the Application menu. -->
<menupopup id="menu_FilePopup">
<menuitem id="menu_preferences"
label="&preferencesCmdMac.label;"
key="key_preferencesCmdMac"
oncommand="Zotero.Utilities.Internal.openPreferences()"/>
<menuitem id="menu_mac_services"
label="&servicesMenuMac.label;"/>
<menuitem id="menu_mac_hide_app"
label="&hideThisAppCmdMac.label;"
key="key_hideThisAppCmdMac"/>
<menuitem id="menu_mac_hide_others"
label="&hideOtherAppsCmdMac.label;"
key="key_hideOtherAppsCmdMac"/>
<menuitem id="menu_mac_show_all"
label="&showAllAppsCmdMac.label;"/>
<menuitem id="menu_FileQuitItem"
label="&quitApplicationCmdMac.label;"
key="key_quitApplication"
command="cmd_quitApplication"/>
</menupopup>
<menu id="windowMenu"
label="&windowMenu.label;"
onpopupshowing="macWindowMenuDidShow();"
onpopuphidden="macWindowMenuDidHide();">
label="&windowMenu.label;"
datasources="rdf:window-mediator" ref="NC:WindowMediatorRoot"
onpopupshowing="macWindowMenuDidShow();"
onpopuphidden="macWindowMenuDidHide();"
hidden="false">
<template>
<rule>
<menupopup>
<menuitem uri="rdf:*"
label="rdf:http://home.netscape.com/NC-rdf#Name"
type="radio"
name="windowList"
oncommand="ShowWindowFromResource(event.target)"/>
</menupopup>
</rule>
</template>
<menupopup id="windowPopup">
<menuitem command="minimizeWindow" label="&minimizeWindow.label;" key="key_minimizeWindow"/>
<menuitem command="zoomWindow" label="&zoomWindow.label;"/>
<!-- decomment when "BringAllToFront" is implemented
<menuseparator/>
<menuitem label="&bringAllToFront.label;" disabled="true"/> -->
<menuseparator id="sep-window-list"/>
</menupopup>
</menu>
<!-- endif -->
<menu id="helpMenu" label="&helpMenu.label;" accesskey="&helpMenu.accesskey;">
<menupopup id="mb-help-popup">
@ -251,88 +396,61 @@
</toolbar>
<hbox flex="1">
<tabbox id="left-tabbox" flex="2" width="300">
<tabs id="tabs" onselect="Scaffold.handleTabSelect(event)">
<tabs id="tabs">
<tab id="tab-metadata" label="&scaffold.tabs.metadata.label;"/>
<tab id="tab-code" label="&scaffold.tabs.code.label;"/>
<tab id="tab-tests" label="&scaffold.tabs.tests.label;"/>
<tab id="tab-browser" label="Browser"/>
<tab id="tab-import" label="&scaffold.tabs.import.label;"/>
</tabs>
<tabpanels flex="1">
<tabpanels flex="1" id="tabpanels">
<tabpanel flex="1" id="tabpanel-metadata">
<grid flex="1">
<columns>
<column/>
<column flex="1"/>
</columns>
<html:div id="metadata-grid">
<label class="label-metadata" value="&scaffold.metadata.translatorID.label;" control="textbox-translatorID"/>
<html:div class="button-row">
<html:input type="text" id="textbox-translatorID"/>
<button label="&scaffold.metadata.translatorID.generate;" oncommand="Scaffold.generateTranslatorID()"/>
</html:div>
<rows>
<row align="center">
<label class="label-metadata" value="&scaffold.metadata.translatorID.label;" control="textbox-translatorID"/>
<hbox>
<textbox id="textbox-translatorID" flex="1"/>
<button label="&scaffold.metadata.translatorID.generate;" oncommand="Scaffold.generateTranslatorID()"/>
</hbox>
</row>
<label class="label-metadata" value="&scaffold.metadata.label.label;" control="textbox-label"/>
<html:input type="text" id="textbox-label" flex="1" value="&scaffold.metadata.label.default;"/>
<row align="center">
<label class="label-metadata" value="&scaffold.metadata.label.label;" control="textbox-label"/>
<textbox id="textbox-label" flex="1" value="&scaffold.metadata.label.default;"/>
</row>
<label class="label-metadata" value="&scaffold.metadata.creator.label;" control="textbox-creator"/>
<html:input type="text" id="textbox-creator" flex="1"/>
<row align="center">
<label class="label-metadata" value="&scaffold.metadata.creator.label;" control="textbox-creator"/>
<textbox id="textbox-creator" flex="1"/>
</row>
<label class="label-metadata" value="&scaffold.metadata.target.label;" control="textbox-target"/>
<html:div class="button-row">
<html:input type="text" id="textbox-target" flex="1"/>
<button label="&scaffold.metadata.target.testRegex;" oncommand="Scaffold.logTargetRegex()"/>
</html:div>
<row align="center">
<label class="label-metadata" value="&scaffold.metadata.target.label;" control="textbox-target"/>
<hbox>
<textbox id="textbox-target" flex="1"/>
<button label="&scaffold.metadata.target.testRegex;" oncommand="Scaffold.logTargetRegex()"/>
</hbox>
</row>
<label class="label-metadata" value="&scaffold.metadata.targetAll.label;" control="textbox-target-all" style="display: none"/>
<html:input type="text" id="textbox-target-all" flex="1" style="display: none"/>
<row align="center">
<label class="label-metadata" value="&scaffold.metadata.targetAll.label;" control="textbox-target-all" style="display: none"/>
<textbox id="textbox-target-all" flex="1" style="display: none"/>
</row>
<label class="label-metadata" value="&scaffold.metadata.configOptions.label;" control="textbox-configOptions"/>
<html:input type="text" id="textbox-configOptions" flex="1"/>
<row align="center">
<label class="label-metadata" value="&scaffold.metadata.configOptions.label;" control="textbox-configOptions"/>
<textbox id="textbox-configOptions" flex="1"/>
</row>
<label class="label-metadata" value="&scaffold.metadata.displayOptions.label;" control="textbox-displayOptions"/>
<html:input type="text" id="textbox-displayOptions" flex="1"/>
<row align="center">
<label class="label-metadata" value="&scaffold.metadata.displayOptions.label;" control="textbox-displayOptions"/>
<textbox id="textbox-displayOptions" flex="1"/>
</row>
<label class="label-metadata" value="&scaffold.metadata.minVersion.label;" control="textbox-minVersion"/>
<html:div>
<html:input type="text" id="textbox-minVersion" flex="1" value="5.0"/>
<label class="label-metadata" value="&scaffold.metadata.priority.label;" control="textbox-priority"/>
<html:input type="text" id="textbox-priority" flex="1" value="&scaffold.metadata.priority.default;"/>
</html:div>
<row align="center">
<label class="label-metadata" value="&scaffold.metadata.minVersion.label;" control="textbox-minVersion"/>
<hbox>
<textbox id="textbox-minVersion" flex="1" value="5.0"/>
<label class="label-metadata" value="&scaffold.metadata.priority.label;" control="textbox-priority"/>
<textbox id="textbox-priority" flex="1" value="&scaffold.metadata.priority.default;"/>
</hbox>
</row>
<label class="label-metadata" value="&scaffold.metadata.hiddenPrefs.label;" control="textbox-hidden-prefs" style="display: none"/>
<html:input type="text" id="textbox-hidden-prefs" flex="1" style="display: none"/>
<row align="center">
<label class="label-metadata" value="&scaffold.metadata.hiddenPrefs.label;" control="textbox-hidden-prefs" style="display: none"/>
<textbox id="textbox-hidden-prefs" flex="1" style="display: none"/>
</row>
<row align="center">
<label class="label-metadata" value="&scaffold.metadata.translatorType.label;"/>
<hbox id="checkboxes-translatorType">
<checkbox id="checkbox-import" label="&scaffold.metadata.translatorType.import;"/>
<checkbox id="checkbox-export" label="&scaffold.metadata.translatorType.export;"/>
<checkbox id="checkbox-web" label="&scaffold.metadata.translatorType.web;" checked="true"/>
<checkbox id="checkbox-search" label="&scaffold.metadata.translatorType.search;"/>
</hbox>
</row>
</rows>
</grid>
<label class="label-metadata" value="&scaffold.metadata.translatorType.label;"/>
<html:div id="checkboxes-translatorType">
<checkbox id="checkbox-import" label="&scaffold.metadata.translatorType.import;"/>
<checkbox id="checkbox-export" label="&scaffold.metadata.translatorType.export;"/>
<checkbox id="checkbox-web" label="&scaffold.metadata.translatorType.web;" checked="true"/>
<checkbox id="checkbox-search" label="&scaffold.metadata.translatorType.search;"/>
</html:div>
</html:div>
</tabpanel>
<tabpanel flex="1" id="tabpanel-code">
<vbox flex="1">
@ -342,24 +460,24 @@
<tabpanel flex="1" id="tabpanel-tests">
<vbox flex="1">
<vbox flex="1">
<hbox flex="1" context="testing-context-menue">
<keyset>
<key id="key-delete-tests" observes="validate-tests" keycode="VK_BACK" oncommand="Scaffold.deleteSelectedTests()"/>
</keyset>
<keyset>
<key id="key-delete-tests" observes="validate-tests" keycode="VK_BACK" oncommand="Scaffold.deleteSelectedTests()"/>
</keyset>
<listbox id="testing-listbox" observes="validate-tests" flex="1" seltype="multiple" onselect="Scaffold.handleTestSelect(event)">
<listhead>
<listheader label="&scaffold.testing.input.label;"/>
<listheader label="&scaffold.testing.status.label;"/>
<listheader label="&scaffold.testing.defer.label;"/>
</listhead>
<listcols>
<listcol flex="1"/>
<listcol class="listcol-testMessage"/>
<listcol/>
</listcols>
</listbox>
</hbox>
<listheader>
<treecol label="&scaffold.testing.input.label;" flex="1"/>
<treecol label="&scaffold.testing.status.label;" width="150"/>
<treecol label="&scaffold.testing.defer.label;"/>
</listheader>
<richlistbox
id="testing-listbox"
class="theme-listbox"
observes="validate-tests"
flex="1"
seltype="multiple"
onselect="Scaffold.handleTestSelect(event)"
context="testing-context-menu"
/>
<hbox>
<button observes="validate-tests" label="&scaffold.testing.delete;" tooltiptext="Delete the selected tests" oncommand="Scaffold.deleteSelectedTests()"/>
<button observes="validate-tests" label="&scaffold.testing.run;" tooltiptext="Run the selected tests" oncommand="Scaffold.runSelectedTests()"/>
@ -374,13 +492,18 @@
<vbox flex="1">
<hbox align="center">
<label control="textbox-tabUrl" value="&scaffold.tabUrl.label;"/>
<menulist id="browser-url" editable="true" flex="1">
<menupopup></menupopup>
</menulist>
<html:input id="browser-url" style="-moz-box-flex: 1;"/>
<button observes="validate-tests" label="&scaffold.testing.create.web;" tooltiptext="Create a new test from the current page" oncommand="Scaffold.saveTestFromCurrent('web')"/>
</hbox>
<browser id="browser" src="about:blank" type="content" flex="1"></browser>
<browser
id="browser"
type="content"
remote="false"
disablehistory="true"
src="about:blank"
maychangeremoteness="true"
flex="1"/>
</vbox>
</tabpanel>
<tabpanel flex="1" id="tabpanel-import">
@ -397,13 +520,7 @@
<splitter resizeafter="farthest" />
<vbox id="right-pane" flex="1">
<hbox id="hbox-testFrame" width="300">
<label control="menulist-testFrame" id="label-testFrame" value="&scaffold.testFrame.label;"/>
<menulist id="menulist-testFrame"/>
</hbox>
<textbox flex="1" id="output" multiline="true" readonly="true"/>
<html:textarea style="-moz-box-flex: 1" id="output" readonly="true"/>
</vbox>
</hbox>

View file

@ -26,7 +26,7 @@
/**
* @namespace Singleton to interface with the browser when ingesting data
*/
var Zotero_Ingester_Interface_SelectItems = function() {}
var Zotero_Ingester_Interface_SelectItems = function () {};
//////////////////////////////////////////////////////////////////////////////
//
@ -38,7 +38,10 @@ var Zotero_Ingester_Interface_SelectItems = function() {}
* Presents items to select in the select box. Assumes window.arguments[0].dataIn is an object with
* URLs as keys and descriptions as values
*/
Zotero_Ingester_Interface_SelectItems.init = function() {
Zotero_Ingester_Interface_SelectItems.init = function () {
document.addEventListener('dialogaccept',
() => Zotero_Ingester_Interface_SelectItems.acceptSelection());
// Set font size from pref
var sbc = document.getElementById('zotero-select-items-container');
Zotero.setFontSize(sbc);
@ -46,59 +49,66 @@ Zotero_Ingester_Interface_SelectItems.init = function() {
this.io = window.arguments[0];
var listbox = document.getElementById("zotero-selectitems-links");
for(var i in this.io.dataIn) { // we could use a tree for this if we wanted to
for (var i in this.io.dataIn) { // we could use a tree for this if we wanted to
var item = this.io.dataIn[i];
var title, checked = false;
if(item && typeof(item) == "object" && item.title !== undefined) {
if (item && typeof item == "object" && item.title !== undefined) {
title = item.title;
checked = !!item.checked;
} else {
}
else {
title = item;
}
var itemNode = document.createElement("listitem");
itemNode.setAttribute("type", "checkbox");
let itemNode = document.createXULElement("richlistitem");
let checkbox = document.createXULElement("checkbox");
checkbox.checked = checked;
checkbox.label = title;
itemNode.setAttribute("value", i);
itemNode.setAttribute("label", title);
itemNode.setAttribute("checked", checked);
listbox.appendChild(itemNode);
itemNode.append(checkbox);
itemNode.addEventListener('click', (event) => {
if (event.target == itemNode) {
checkbox.checked = !checkbox.checked;
}
});
listbox.append(itemNode);
}
// Check item if there is only one
if (listbox.itemCount === 1) {
listbox.getItemAtIndex(0).setAttribute("checked", true);
listbox.getItemAtIndex(0).firstElementChild.checked = true;
}
}
};
/**
* Selects or deselects all items
* @param {Boolean} deselect If true, deselect all items instead of selecting all items
*/
Zotero_Ingester_Interface_SelectItems.selectAll = function(deselect) {
Zotero_Ingester_Interface_SelectItems.selectAll = function (deselect) {
var listbox = document.getElementById("zotero-selectitems-links");
for (var i=0; i<listbox.childNodes.length; i++){
listbox.childNodes[i].setAttribute('checked', !deselect);
for (var i = 0; i < listbox.childNodes.length; i++) {
listbox.childNodes[i].firstElementChild.checked = !deselect;
}
}
};
/**
* Called when "OK" button is pressed to populate window.arguments[0].dataOut with selected items
*/
Zotero_Ingester_Interface_SelectItems.acceptSelection = function() {
Zotero_Ingester_Interface_SelectItems.acceptSelection = function () {
var listbox = document.getElementById("zotero-selectitems-links");
var returnObject = false;
this.io.dataOut = new Object();
this.io.dataOut = {};
// collect scrapeURLList from listbox
for(var i=0; i<listbox.childNodes.length; i++) {
for (var i = 0; i < listbox.childNodes.length; i++) {
var itemNode = listbox.childNodes[i];
if(itemNode.getAttribute("checked") == "true") {
this.io.dataOut[itemNode.getAttribute("value")] = itemNode.getAttribute("label");
if (itemNode.firstElementChild.checked) {
this.io.dataOut[itemNode.getAttribute("value")] = itemNode.firstElementChild.getAttribute("label");
returnObject = true;
}
}
if(!returnObject) this.io.dataOut = null;
}
if (!returnObject) this.io.dataOut = null;
};

View file

@ -25,27 +25,33 @@
-->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
<dialog xmlns:html="http://www.w3.org/1999/xhtml"
<window xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&zotero.selectitems.title;" width="400" height="330"
persist="width height screenX screenY"
buttons="cancel,accept"
ondialogaccept="Zotero_Ingester_Interface_SelectItems.acceptSelection()"
id="zotero-selectitems"
onload="Zotero_Ingester_Interface_SelectItems.init()">
<script src="../include.js"/>
<script src="selectitems.js"/>
<dialog
buttons="cancel,accept"
id="zotero-selectitems">
<script>
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
Services.scriptloader.loadSubScript("chrome://zotero/content/include.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/ingester/selectitems.js", this);
Services.scriptloader.loadSubScript("chrome://global/content/customElements.js", this);
</script>
<vbox id="zotero-select-items-container" flex="1">
<caption label="&zotero.selectitems.intro.label;" id="zotero-selectitems-intro"/>
<box flex="1">
<listbox id="zotero-selectitems-links" flex="1" context="zoteroSelectContext"></listbox>
<richlistbox id="zotero-selectitems-links" flex="1"></richlistbox>
</box>
<hbox>
<button label="Select All" oncommand="Zotero_Ingester_Interface_SelectItems.selectAll()"/>
<button label="Deselect All" oncommand="Zotero_Ingester_Interface_SelectItems.selectAll(true)"/>
</hbox>
</vbox>
</dialog>
</dialog>
</window>

View file

@ -912,7 +912,7 @@ function openStyleEditor() {
function openScaffold() {
openWindowByType(
'chrome://scaffold/content/scaffold.xul',
'chrome://scaffold/content/scaffold.xhtml',
'zotero:scaffold',
'chrome,resizable'
);

View file

@ -164,7 +164,7 @@ Zotero.CookieSandbox.prototype = {
* @param {nsIInterfaceRequestor} ir
*/
"attachToInterfaceRequestor": function(ir) {
Zotero.CookieSandbox.Observer.trackedInterfaceRequestors.push(Components.utils.getWeakReference(ir.QueryInterface(Components.interfaces.nsIInterfaceRequestor)));
Zotero.CookieSandbox.Observer.trackedInterfaceRequestors.push(Cu.getWeakReference(ir.getInterface(Ci.nsIInterfaceRequestor)));
Zotero.CookieSandbox.Observer.trackedInterfaceRequestorSandboxes.push(this);
},

View file

@ -1,6 +1,3 @@
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
@namespace html url("http://www.w3.org/1999/xhtml");
#tb-new {
list-style-image: url('chrome://scaffold/skin/new.png');
}
@ -53,11 +50,28 @@
list-style-image: url('chrome://scaffold/skin/add.png');
}
#tabpanel-metadata textbox {
height:2em;
#metadata-grid {
-moz-box-flex: 1;
display: grid;
align-content: start;
align-items: center;
grid-template-columns: max-content 1fr;
}
#tabpanel-metadata hbox, #hbox-testFrame {
#metadata-grid > label:nth-child(2n + 1) {
justify-self: right;
}
.button-row {
display: grid;
grid-template-columns: 1fr max-content;
}
#tabpanel-metadata input {
height: 1.5em;
}
#hbox-testFrame {
-moz-box-align: center;
}
@ -65,7 +79,7 @@
height:200px;
}
listbox {
richlistbox {
min-width:200px;
}
@ -95,12 +109,50 @@ vbox > splitter {
cursor: row-resize;
}
listbox[disabled], toolbarbutton[disabled] {
/* Adapted from Mozilla common-shared.css */
/* https://searchfox.org/mozilla-central/rev/2f5ed7b7244172d46f538051250b14fb4d8f1a5f/toolkit/themes/shared/in-content/common-shared.css */
select[size][multiple],
listheader,
richlistbox {
appearance: none;
margin-inline: 0;
background-color: #fff;
border: 1px solid #ccc;
}
listheader {
padding-bottom: 1px;
}
treecol {
appearance: none;
border: none;
}
listheader + richlistbox {
margin-top: 0;
border-top: none;
}
select[size][multiple] > option,
treechildren::-moz-tree-row {
padding: 0.3em;
margin: 0;
border: none;
background-image: none;
}
richlistitem[selected] menulist:focus-visible {
outline-offset: -2px;
}
richlistbox[disabled], toolbarbutton[disabled] {
opacity: 0.6;
pointer-events: none;
}
dialog listbox > listitem[disabled] {
dialog richlistbox > richlistitem[disabled] {
font-weight: 600;
}
@ -110,12 +162,14 @@ dialog listbox > listitem[disabled] {
#checkboxes-translatorType checkbox {
margin-right: 10px;
display: inline-block;
}
#tabpanel-metadata label:first-child {
text-align: right;
}
.listcol-testMessage {
width: 200px;
#right-pane textarea {
margin-right: 5px;
margin-bottom: 5px;
}

View file

@ -111,7 +111,11 @@ zoterofilesyncstatus {
richlistitem {
padding: 0.2em 0.4em;
}
richlistitem, richlistitem > * {
overflow-x: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}