From 2dcf358eb578297d94762dfc875283834c05597a Mon Sep 17 00:00:00 2001 From: j Date: Thu, 24 Jan 2019 18:19:01 +0530 Subject: [PATCH 01/15] fix debug --- oml/item/icons.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oml/item/icons.py b/oml/item/icons.py index 7d96d39..e87b724 100644 --- a/oml/item/icons.py +++ b/oml/item/icons.py @@ -105,7 +105,7 @@ class Icons(dict): c.close() conn.close() except: - logger.debug('failed to clear icon %s', id) + logger.debug('failed to clear icon %s', prefix) def vacuum(self, ids): conn = self.connect() From 57692665a474d2200eb1ebb0366de9fdcfec5e8c Mon Sep 17 00:00:00 2001 From: j Date: Thu, 24 Jan 2019 18:19:21 +0530 Subject: [PATCH 02/15] store comments --- static/js/annotation.js | 28 ++++++++++++++++++++++++---- static/js/viewer.js | 13 +++++++++++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/static/js/annotation.js b/static/js/annotation.js index d0d26b1..2a4df4d 100644 --- a/static/js/annotation.js +++ b/static/js/annotation.js @@ -1,14 +1,34 @@ 'use strict'; -oml.ui.annotation = function(data, $iframe) { +oml.ui.annotation = function(annotation, $iframe) { var $arrayEditable = Ox.ArrayEditable({ editing: true, + items: (annotation.comments || []).map(function(comment) { + comment.editable = true + return comment + }), type: 'textarea' }).css({ minHeight: '16px' + }).bindEvent({ + submit: function(data) { + var comment = Ox.getObjectById(annotation.comments, data.id) + if (comment) { + comment.value = data.value + comment.modified = (new Date).toISOString() + } else { + annotation.comments.push({ + created: data.created || (new Date).toISOString(), + modified: (new Date).toISOString(), + id: data.id, + value: data.value + }) + } + that.triggerEvent('change') + } }); var that = Ox.Element().attr({ - id: 'a-' + data.id + id: 'a-' + annotation.id }).addClass( 'OxSelectable' ).css({ @@ -21,10 +41,10 @@ oml.ui.annotation = function(data, $iframe) { fontSize: '14px', lineHeight: '21px', padding: '8px' - }).html(Ox.encodeHTMLEntities(data.text).replace(/\n/g, '
')).on({ + }).html(Ox.encodeHTMLEntities(annotation.text).replace(/\n/g, '
')).on({ click: function(event) { $iframe.postMessage('selectAnnotation', { - id: data.id + id: annotation.id }) } }) diff --git a/static/js/viewer.js b/static/js/viewer.js index 8cb4842..848364a 100644 --- a/static/js/viewer.js +++ b/static/js/viewer.js @@ -46,6 +46,7 @@ oml.ui.viewer = function() { function saveAnnotations(data) { if (data) { data.created = data.created || (new Date).toISOString(); + data.comments = data.comments || []; annotations.push(data); } localStorage[item + '.annotations'] = JSON.stringify(annotations) @@ -57,6 +58,14 @@ oml.ui.viewer = function() { saveAnnotations() } + var annotationEvents = { + change: function() { + console.log('change...') + console.log(annotations) + saveAnnotations() + } + } + that.updateElement = function() { item = ui.item; if (item && item.length) { @@ -75,7 +84,7 @@ oml.ui.viewer = function() { if (event == 'addAnnotation') { console.log('adding', data.id) saveAnnotations(data); - var $annotation = oml.ui.annotation(data, $iframe) + var $annotation = oml.ui.annotation(data, $iframe).bindEvent(annotationEvents) oml.$ui.annotationFolder.append($annotation); $annotation.annotate(); } else if (event == 'removeAnnotation') { @@ -93,7 +102,7 @@ oml.ui.viewer = function() { init: function() { loadAnnotations(function(annotations) { annotations.forEach(function(data) { - var $annotation = oml.ui.annotation(data, $iframe) + var $annotation = oml.ui.annotation(data, $iframe).bindEvent(annotationEvents) oml.$ui.annotationFolder.append($annotation); }) // fixme: trigger loaded event from reader instead? From 76c9bf5d6eef418224e47030083f11093407ea0a Mon Sep 17 00:00:00 2001 From: j Date: Thu, 24 Jan 2019 18:27:26 +0530 Subject: [PATCH 03/15] fix remove --- static/reader/pdf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/reader/pdf.js b/static/reader/pdf.js index b4bbed3..6e095f1 100644 --- a/static/reader/pdf.js +++ b/static/reader/pdf.js @@ -144,7 +144,7 @@ function removeAnnotation(id) { annotations = annotations.filter(function(annotation) { return annotation.id != id }) - Ox.$parent.postMessage('removeAnnotation', {id: selected.dataset.id}) + Ox.$parent.postMessage('removeAnnotation', {id: id}) } function loadAnnotations(page) { From a7eb03742c5a6e72b81a6732c0d6ddbaa1e09e2c Mon Sep 17 00:00:00 2001 From: j Date: Thu, 24 Jan 2019 18:34:06 +0530 Subject: [PATCH 04/15] scroll --- static/js/annotationFolder.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/js/annotationFolder.js b/static/js/annotationFolder.js index 0472bce..b9d7760 100644 --- a/static/js/annotationFolder.js +++ b/static/js/annotationFolder.js @@ -3,8 +3,9 @@ oml.ui.annotationFolder = function() { var ui = oml.user.ui, that = Ox.Element().css({ overflowY: 'auto', + overflowX: 'hidden', }); return that; -}; \ No newline at end of file +}; From b4c6f2b4ac6f2d556f4c9f209c31ac47813ec478 Mon Sep 17 00:00:00 2001 From: j Date: Thu, 24 Jan 2019 18:36:20 +0530 Subject: [PATCH 05/15] use .editorconfig --- .editorconfig | 12 ++++++++++++ oml/__main__.py | 1 - oml/api.py | 1 - oml/changelog.py | 1 - oml/commands.py | 1 - oml/downloads.py | 1 - oml/item/api.py | 1 - oml/item/handlers.py | 1 - oml/item/icons.py | 1 - oml/item/models.py | 1 - oml/item/person.py | 1 - oml/item/person_api.py | 1 - oml/item/query.py | 1 - oml/item/scan.py | 1 - oml/item/title_api.py | 1 - oml/library.py | 1 - oml/localnodes.py | 1 - oml/media/__init__.py | 1 - oml/media/cbr.py | 1 - oml/media/epub.py | 1 - oml/media/opf.py | 1 - oml/media/pdf.py | 1 - oml/media/txt.py | 1 - oml/meta/__init__.py | 1 - oml/meta/duckduckgo.py | 1 - oml/meta/google.py | 1 - oml/meta/utils.py | 1 - oml/node/cert.py | 1 - oml/node/nodeapi.py | 1 - oml/node/server.py | 1 - oml/nodes.py | 1 - oml/oxtornado.py | 1 - oml/pdict.py | 1 - oml/queryparser.py | 1 - oml/server.py | 1 - oml/settings.py | 1 - oml/setup.py | 1 - oml/ssl_request.py | 1 - oml/tasks.py | 1 - oml/tor_request.py | 1 - oml/ui.py | 1 - oml/ui_websocket.py | 1 - oml/update.py | 1 - oml/user/api.py | 1 - oml/user/models.py | 1 - oml/utils.py | 1 - oml/websocket.py | 1 - 47 files changed, 12 insertions(+), 46 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..28971c5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +[*] +end_of_line = lf +insert_final_newline = true + +[*.{js,py,html}] +indent_style = space +indent_size = 4 +charset = utf-8 + +[Makefile] +indent_style = tab + diff --git a/oml/__main__.py b/oml/__main__.py index f888dd5..f5e2b5e 100644 --- a/oml/__main__.py +++ b/oml/__main__.py @@ -1,6 +1,5 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import os import sys diff --git a/oml/api.py b/oml/api.py index 32b31bc..bf44566 100644 --- a/oml/api.py +++ b/oml/api.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from os.path import normpath, dirname, abspath, join diff --git a/oml/changelog.py b/oml/changelog.py index 483a708..c16834c 100644 --- a/oml/changelog.py +++ b/oml/changelog.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import os from datetime import datetime diff --git a/oml/commands.py b/oml/commands.py index 7e0a05d..d147b2e 100644 --- a/oml/commands.py +++ b/oml/commands.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from os.path import join, exists, dirname diff --git a/oml/downloads.py b/oml/downloads.py index 8374f95..1835ec1 100644 --- a/oml/downloads.py +++ b/oml/downloads.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import os from threading import Thread diff --git a/oml/item/api.py b/oml/item/api.py index c94d7cf..d0c04a5 100644 --- a/oml/item/api.py +++ b/oml/item/api.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import json import hashlib import os diff --git a/oml/item/handlers.py b/oml/item/handlers.py index b13c9f4..2dd4dcf 100644 --- a/oml/item/handlers.py +++ b/oml/item/handlers.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from datetime import datetime diff --git a/oml/item/icons.py b/oml/item/icons.py index e87b724..dc670d5 100644 --- a/oml/item/icons.py +++ b/oml/item/icons.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import os import sqlite3 diff --git a/oml/item/models.py b/oml/item/models.py index b2d5c3a..cfc44e3 100644 --- a/oml/item/models.py +++ b/oml/item/models.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from datetime import datetime import base64 import hashlib diff --git a/oml/item/person.py b/oml/item/person.py index 4e68205..27ad8f7 100644 --- a/oml/item/person.py +++ b/oml/item/person.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import unicodedata diff --git a/oml/item/person_api.py b/oml/item/person_api.py index e6cb8dc..1293f34 100644 --- a/oml/item/person_api.py +++ b/oml/item/person_api.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import unicodedata from oxtornado import actions diff --git a/oml/item/query.py b/oml/item/query.py index b48fe29..9683dee 100644 --- a/oml/item/query.py +++ b/oml/item/query.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 #does not work in sqlite diff --git a/oml/item/scan.py b/oml/item/scan.py index 04a58c9..4869ac9 100644 --- a/oml/item/scan.py +++ b/oml/item/scan.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from datetime import datetime diff --git a/oml/item/title_api.py b/oml/item/title_api.py index b34a70c..22fb3b5 100644 --- a/oml/item/title_api.py +++ b/oml/item/title_api.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import hashlib import json import unicodedata diff --git a/oml/library.py b/oml/library.py index 510e7d2..c80fc4d 100644 --- a/oml/library.py +++ b/oml/library.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import json import os import time diff --git a/oml/localnodes.py b/oml/localnodes.py index 5e7129b..f3fa7ec 100644 --- a/oml/localnodes.py +++ b/oml/localnodes.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import socket diff --git a/oml/media/__init__.py b/oml/media/__init__.py index 5cebfcf..dd9d32b 100644 --- a/oml/media/__init__.py +++ b/oml/media/__init__.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import base64 diff --git a/oml/media/cbr.py b/oml/media/cbr.py index 9f72a71..66e1324 100644 --- a/oml/media/cbr.py +++ b/oml/media/cbr.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import os diff --git a/oml/media/epub.py b/oml/media/epub.py index 81ccb17..cd5a6f0 100644 --- a/oml/media/epub.py +++ b/oml/media/epub.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import os diff --git a/oml/media/opf.py b/oml/media/opf.py index b8a20b8..8070c40 100644 --- a/oml/media/opf.py +++ b/oml/media/opf.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import xml.etree.ElementTree as ET diff --git a/oml/media/pdf.py b/oml/media/pdf.py index c4e9719..bc342d0 100644 --- a/oml/media/pdf.py +++ b/oml/media/pdf.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import sys diff --git a/oml/media/txt.py b/oml/media/txt.py index 193441c..ca76253 100644 --- a/oml/media/txt.py +++ b/oml/media/txt.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import os diff --git a/oml/meta/__init__.py b/oml/meta/__init__.py index 45c3d3e..13209c0 100644 --- a/oml/meta/__init__.py +++ b/oml/meta/__init__.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import stdnum.isbn diff --git a/oml/meta/duckduckgo.py b/oml/meta/duckduckgo.py index 105426b..b3c28e6 100644 --- a/oml/meta/duckduckgo.py +++ b/oml/meta/duckduckgo.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import ox.web.duckduckgo diff --git a/oml/meta/google.py b/oml/meta/google.py index 4b628bf..713647d 100644 --- a/oml/meta/google.py +++ b/oml/meta/google.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from time import time, sleep from urllib.parse import urlencode diff --git a/oml/meta/utils.py b/oml/meta/utils.py index 49f0703..741fce8 100644 --- a/oml/meta/utils.py +++ b/oml/meta/utils.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import re diff --git a/oml/node/cert.py b/oml/node/cert.py index 64cb741..8b7e5d1 100644 --- a/oml/node/cert.py +++ b/oml/node/cert.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import hashlib import os diff --git a/oml/node/nodeapi.py b/oml/node/nodeapi.py index a3f16d5..3992ddd 100644 --- a/oml/node/nodeapi.py +++ b/oml/node/nodeapi.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from user.models import User diff --git a/oml/node/server.py b/oml/node/server.py index 4673441..0e5694e 100644 --- a/oml/node/server.py +++ b/oml/node/server.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from socketserver import ThreadingMixIn from threading import Thread import base64 diff --git a/oml/nodes.py b/oml/nodes.py index 70fae05..1933def 100644 --- a/oml/nodes.py +++ b/oml/nodes.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from queue import Queue diff --git a/oml/oxtornado.py b/oml/oxtornado.py index 7215e9a..b34cdfe 100644 --- a/oml/oxtornado.py +++ b/oml/oxtornado.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from contextlib import contextmanager diff --git a/oml/pdict.py b/oml/pdict.py index a648d24..cf1d37b 100644 --- a/oml/pdict.py +++ b/oml/pdict.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import os import json diff --git a/oml/queryparser.py b/oml/queryparser.py index 5ccc68a..033e068 100644 --- a/oml/queryparser.py +++ b/oml/queryparser.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from datetime import datetime import unicodedata diff --git a/oml/server.py b/oml/server.py index f98ed7b..b01d598 100644 --- a/oml/server.py +++ b/oml/server.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import os import sys diff --git a/oml/settings.py b/oml/settings.py index 631b398..f70b930 100644 --- a/oml/settings.py +++ b/oml/settings.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import json import os diff --git a/oml/setup.py b/oml/setup.py index 85c2bae..b243a00 100644 --- a/oml/setup.py +++ b/oml/setup.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import os diff --git a/oml/ssl_request.py b/oml/ssl_request.py index 538ea75..43b4fb3 100644 --- a/oml/ssl_request.py +++ b/oml/ssl_request.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import ssl import http.client diff --git a/oml/tasks.py b/oml/tasks.py index af02c3c..3f97379 100644 --- a/oml/tasks.py +++ b/oml/tasks.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from queue import PriorityQueue from threading import Thread import json diff --git a/oml/tor_request.py b/oml/tor_request.py index 7b10063..0fd6455 100644 --- a/oml/tor_request.py +++ b/oml/tor_request.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import ssl import http.client diff --git a/oml/ui.py b/oml/ui.py index a15dfac..ba4630a 100644 --- a/oml/ui.py +++ b/oml/ui.py @@ -1,5 +1,4 @@ # encoding: utf-8 -# vi:si:et:sw=4:sts=4:ts=4 import sys import os try: diff --git a/oml/ui_websocket.py b/oml/ui_websocket.py index 7efdddd..3bd5204 100644 --- a/oml/ui_websocket.py +++ b/oml/ui_websocket.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 import json diff --git a/oml/update.py b/oml/update.py index 8ab472c..83c490a 100644 --- a/oml/update.py +++ b/oml/update.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from contextlib import closing import base64 diff --git a/oml/user/api.py b/oml/user/api.py index d8fecfa..6764050 100644 --- a/oml/user/api.py +++ b/oml/user/api.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from copy import deepcopy diff --git a/oml/user/models.py b/oml/user/models.py index ad75f0d..bc2d072 100644 --- a/oml/user/models.py +++ b/oml/user/models.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from datetime import datetime import json import os diff --git a/oml/utils.py b/oml/utils.py index e5b9337..5130156 100644 --- a/oml/utils.py +++ b/oml/utils.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from datetime import datetime diff --git a/oml/websocket.py b/oml/websocket.py index 90821bb..27a3d4e 100644 --- a/oml/websocket.py +++ b/oml/websocket.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 from tornado.websocket import WebSocketHandler From ebff5a81532b99a8b5f80092448e153bfe50e739 Mon Sep 17 00:00:00 2001 From: j Date: Fri, 25 Jan 2019 14:47:36 +0530 Subject: [PATCH 06/15] fix UI path with space --- ctl | 2 +- oml/ui.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ctl b/ctl index 805595c..1abac88 100755 --- a/ctl +++ b/ctl @@ -187,7 +187,7 @@ fi if [ "$1" == "ui" ]; then shift - $PYTHON "$NAME/oml/ui.py" $@ + $PYTHON "$NAME/oml/ui.py" "$@" exit $? fi if [ "$1" == "init" ]; then diff --git a/oml/ui.py b/oml/ui.py index ba4630a..5b9dc26 100644 --- a/oml/ui.py +++ b/oml/ui.py @@ -104,7 +104,8 @@ if __name__ == '__main__': if len(sys.argv) >= 3: base = sys.argv[2] base = os.path.expanduser(base) - os.chdir(base) + if os.path.exists(base): + os.chdir(base) if len(sys.argv) >= 2 and sys.argv[1] == 'folder': print(ui.selectFolder({})) else: From 0bfbebb21d4989bdabd90580531f9f91d543f865 Mon Sep 17 00:00:00 2001 From: j Date: Fri, 25 Jan 2019 16:36:54 +0530 Subject: [PATCH 07/15] gtkstatus is broken, just launch oml for now --- ctl | 10 ++++++---- oml/gtkstatus.py | 14 +++++++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/ctl b/ctl index 1abac88..4c05511 100755 --- a/ctl +++ b/ctl @@ -162,12 +162,14 @@ if [ "$1" == "open" ]; then xdg-open "file://${BASE}/openmedialibrary/static/html/load.html" fi else - $PYTHON "${NAME}/oml/gtkstatus.py" $@ - exit $? + #$PYTHON "${NAME}/oml/gtkstatus.py" $@ + #exit $? + "$0" start & fi else - $PYTHON "$NAME/oml/gtkstatus.py" $@ - exit $? + #$PYTHON "$NAME/oml/gtkstatus.py" $@ + #exit $? + "$0" start & fi fi exit 0 diff --git a/oml/gtkstatus.py b/oml/gtkstatus.py index 726afc5..d9abb0d 100644 --- a/oml/gtkstatus.py +++ b/oml/gtkstatus.py @@ -22,6 +22,18 @@ title = "Open Media Library" ctl = base + '/ctl' +def check_pid(pid): + if not os.path.exists(pid): + return False + try: + with open(pid, 'r') as fd: + pid = int(fd.read().strip()) + os.kill(pid, 0) + except OSError: + return False + else: + return True + class OMLIcon: menu = None icon = None @@ -81,7 +93,7 @@ class OMLIcon: @classmethod def is_running(cls): pid = cls.get_pid() - if pid and os.path.exists(pid): + if pid and check_pid(pid): return True else: return False From d97d396ef306a4bba9ab156f470c9db6d435d4d0 Mon Sep 17 00:00:00 2001 From: j Date: Fri, 25 Jan 2019 16:38:45 +0530 Subject: [PATCH 08/15] scroll select annotation into view --- static/reader/pdf.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/static/reader/pdf.js b/static/reader/pdf.js index 6e095f1..15b4487 100644 --- a/static/reader/pdf.js +++ b/static/reader/pdf.js @@ -11,14 +11,21 @@ Ox.load({ console.log('got', event, 'data', data) if (event == 'selectAnnotation') { var annotation = annotations.filter(function(a) { return a.id == data.id })[0] + var delay = 0 if ( annotation && annotation.page && PDFViewerApplication.pdfViewer.currentPageNumber != annotation.page ) { - //FIXME: scroll into view PDFViewerApplication.pdfViewer.currentPageNumber = annotation.page; + delay = 250 } + setTimeout(function() { + var el = document.querySelector('.a' + annotation.id); + if (el) { + document.querySelector('#viewerContainer').scrollTop = el.offsetTop + el.parentElement.offsetTop - 64; + } + }, delay) selectAnnotation(data.id) } else if (event == 'addAnnotations') { data.annotations.forEach(function(annotation) { From 2713c175506260c5387c2271c5dd44108fe0c526 Mon Sep 17 00:00:00 2001 From: j Date: Fri, 25 Jan 2019 18:42:49 +0530 Subject: [PATCH 09/15] make sure folder exists --- oml/item/icons.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/oml/item/icons.py b/oml/item/icons.py index dc670d5..37d89fb 100644 --- a/oml/item/icons.py +++ b/oml/item/icons.py @@ -22,23 +22,32 @@ logger = logging.getLogger(__name__) MAX_WORKERS = 4 - class Icons(dict): + def __init__(self, db): self._db = db self.create() + def is_available(self): + folder = os.path.dirname(self._db) + if os.path.exists(folder): + if not os.path.exists(self._db): + self.create() + return os.path.exists(self._db) + def connect(self): conn = sqlite3.connect(self._db, timeout=90) return conn def create(self): - conn = self.connect() - c = conn.cursor() - c.execute('CREATE TABLE IF NOT EXISTS icon (id varchar(64) unique, data blob)') - c.execute('CREATE TABLE IF NOT EXISTS setting (key varchar(256) unique, value text)') - if int(self.get_setting(c, 'version', 0)) < 1: - self.set_setting(c, 'version', 1) + folder = os.path.dirname(self._db) + if os.path.exists(folder): + conn = self.connect() + c = conn.cursor() + c.execute('CREATE TABLE IF NOT EXISTS icon (id varchar(64) unique, data blob)') + c.execute('CREATE TABLE IF NOT EXISTS setting (key varchar(256) unique, value text)') + if int(self.get_setting(c, 'version', 0)) < 1: + self.set_setting(c, 'version', 1) def get_setting(self, c, key, default=None): c.execute('SELECT value FROM setting WHERE key = ?', (key, )) @@ -139,6 +148,8 @@ def get_icons_db_path(): return icons_db_path def get_icon_sync(id, type_, size): + if not icons.is_available(): + return '' if size: skey = '%s:%s:%s' % (type_, id, size) data = icons[skey] From 269c6e511f7f48ab2f6bc50ad6e7b7887c33305a Mon Sep 17 00:00:00 2001 From: j Date: Fri, 25 Jan 2019 18:45:59 +0530 Subject: [PATCH 10/15] no icon for now --- oml/gtkstatus.py | 47 +++++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/oml/gtkstatus.py b/oml/gtkstatus.py index d9abb0d..4dc3ce5 100644 --- a/oml/gtkstatus.py +++ b/oml/gtkstatus.py @@ -9,15 +9,9 @@ import webbrowser import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GLib -try: - gi.require_version('AppIndicator3', '0.1') - from gi.repository import AppIndicator3 as appindicator -except: - appindicator = None - base = dirname(dirname(dirname(abspath(__file__)))) -icon = os.path.join(base, 'openmedialibrary/static/png/oml.png') +icon = os.path.join(base, 'openmedialibrary/static/svg/oml.svg') title = "Open Media Library" ctl = base + '/ctl' @@ -34,26 +28,29 @@ def check_pid(pid): else: return True -class OMLIcon: +def preexec(): # Don't forward signals. + os.setpgrp() + + +class OMLStatus: menu = None icon = None - indicator = None def __init__(self, autostart=False): self.autostart = autostart self.create_pid() - if appindicator: - self.indicator = appindicator.Indicator.new("OML", icon, - appindicator.IndicatorCategory.APPLICATION_STATUS) - self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE) - self.menu = self.get_menu() - self.indicator.set_menu(self.menu) - else: - self.icon = Gtk.StatusIcon() - self.icon.set_from_file(icon) - self.icon.set_title(title) - self.icon.connect("activate", self._click) - self.icon.connect("popup-menu", self._click) + self.win = Gtk.Window() + self.win.set_icon_from_file(icon) + self.win.set_title(title) + #self.win.show_all() + self.win.iconify() + ''' + self.icon = Gtk.StatusIcon() + self.icon.set_from_file(icon) + self.icon.set_title(title) + self.icon.connect("activate", self._click) + self.icon.connect("popup-menu", self._click) + ''' subprocess.Popen([ctl, 'start'], close_fds=True, preexec_fn=preexec) if not self.autostart: GLib.timeout_add_seconds(1, self._open, None) @@ -152,16 +149,14 @@ class OMLIcon: url += '#%s' % port webbrowser.open_new_tab(url) -def preexec(): # Don't forward signals. - os.setpgrp() if __name__ == '__main__': import signal autostart = len(sys.argv) > 1 and sys.argv[1] == '--autostart' - if OMLIcon.is_running(): - OMLIcon.load() + if OMLStatus.is_running(): + OMLStatus.load() else: - oml = OMLIcon(autostart) + oml = OMLStatus(autostart) main_loop = GLib.MainLoop() try: main_loop.run() From a691dc2dc85412dd8ad9546d0966b7635756c48d Mon Sep 17 00:00:00 2001 From: j Date: Mon, 28 Jan 2019 15:01:47 +0530 Subject: [PATCH 11/15] fix folder height --- static/js/folders.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/js/folders.js b/static/js/folders.js index d943e62..b34545d 100644 --- a/static/js/folders.js +++ b/static/js/folders.js @@ -362,8 +362,9 @@ oml.ui.folders = function() { oml.$ui.libraryList[index].options({ items: library }); + // library + public + lists oml.$ui.folder[index].$content - .css({height: 16 + items.length * 16 + 'px'}); + .css({height: 16 + 16 + items.length * 16 + 'px'}); oml.$ui.folderList[index].options({ items: items }) From a0a1b21aae9ec4f2eb5f276ce8d01e17f57c9ae7 Mon Sep 17 00:00:00 2001 From: j Date: Mon, 28 Jan 2019 15:02:04 +0530 Subject: [PATCH 12/15] toggle annotations menu --- static/js/mainMenu.js | 1 + 1 file changed, 1 insertion(+) diff --git a/static/js/mainMenu.js b/static/js/mainMenu.js index e8c4c35..45280e2 100644 --- a/static/js/mainMenu.js +++ b/static/js/mainMenu.js @@ -601,6 +601,7 @@ oml.ui.mainMenu = function() { that[data.value ? 'enableItem' : 'disableItem']('book'); that[data.value ? 'disableItem' : 'enableItem']('showfilters'); that[data.value ? 'enableItem' : 'disableItem']('showbrowser'); + that[data.value ? 'enableItem' : 'disableItem']('showannotations'); } }, oml_itemview: function(data) { From b1215fbc1b3ec6aa18fe9138e0251de173d94915 Mon Sep 17 00:00:00 2001 From: j Date: Mon, 28 Jan 2019 15:02:31 +0530 Subject: [PATCH 13/15] add optional username/password protection --- oml/item/handlers.py | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/oml/item/handlers.py b/oml/item/handlers.py index 2dd4dcf..19d6460 100644 --- a/oml/item/handlers.py +++ b/oml/item/handlers.py @@ -6,6 +6,7 @@ import mimetypes import os from urllib.request import quote import zipfile +import base64 import ox @@ -26,7 +27,42 @@ import state import logging logger = logging.getLogger(__name__) -class OMLHandler(tornado.web.RequestHandler): + +class OptionalBasicAuthMixin(object): + class SendChallenge(Exception): + pass + + def prepare(self): + if settings.preferences.get('authentication'): + try: + self.authenticate_user() + except self.SendChallenge: + self.send_auth_challenge() + + def send_auth_challenge(self): + realm = "Open Media Library" + hdr = 'Basic realm="%s"' % realm + self.set_status(401) + self.set_header('www-authenticate', hdr) + self.finish() + return False + + def authenticate_user(self): + auth_header = self.request.headers.get('Authorization') + if not auth_header or not auth_header.startswith('Basic '): + raise self.SendChallenge() + + auth_data = auth_header.split(None, 1)[-1] + auth_data = base64.b64decode(auth_data).decode('ascii') + username, password = auth_data.split(':', 1) + + auth = settings.preferences.get('authentication') + if auth.get('username') == username and auth.get('password') == password: + self._current_user = username + else: + raise self.SendChallenge() + +class OMLHandler(OptionalBasicAuthMixin, tornado.web.RequestHandler): def initialize(self): pass @@ -140,7 +176,7 @@ class ReaderHandler(OMLHandler): path = os.path.join(settings.static_path, html) return serve_static(self, path, 'text/html') -class UploadHandler(tornado.web.RequestHandler): +class UploadHandler(OMLHandler): def initialize(self, context=None): self._context = context From aac3e66f7b28b822a4e108331adf7d945831a203 Mon Sep 17 00:00:00 2001 From: j Date: Mon, 28 Jan 2019 15:39:00 +0530 Subject: [PATCH 14/15] its an inbox --- oml/node/nodeapi.py | 4 +-- oml/nodes.py | 2 +- oml/user/api.py | 13 ++++----- oml/user/models.py | 4 +-- static/js/folderList.js | 2 +- static/js/folders.js | 65 +++++++++++++++++++++-------------------- static/js/mainMenu.js | 6 ++-- static/js/utils.js | 16 ++++++---- 8 files changed, 58 insertions(+), 54 deletions(-) diff --git a/oml/node/nodeapi.py b/oml/node/nodeapi.py index 3992ddd..93c0d49 100644 --- a/oml/node/nodeapi.py +++ b/oml/node/nodeapi.py @@ -81,9 +81,9 @@ def api_upload(user_id, items): from user.models import List peer = User.get(user_id) if peer: - l = List.get_or_create(':Public') + l = List.get_or_create(':Inbox') if l: - logger.debug('%s added items to public folder: %s', user_id, items) + logger.debug('%s added items to inbox: %s', user_id, items) l.add_items(items) trigger_event('change', {}) return True diff --git a/oml/nodes.py b/oml/nodes.py index 1933def..f9b3ff2 100644 --- a/oml/nodes.py +++ b/oml/nodes.py @@ -461,7 +461,7 @@ class Node(Thread): return False def upload(self, items): - logger.debug('add items to %s\'s public folder: %s', self.id, items) + logger.debug('add items to %s\'s inbox: %s', self.user_id, items) r = self.request('upload', items) return bool(r) diff --git a/oml/user/api.py b/oml/user/api.py index 6764050..7f138c8 100644 --- a/oml/user/api.py +++ b/oml/user/api.py @@ -188,7 +188,7 @@ def getLists(data): 'type': 'libraries', 'user': None, }) - List.get_or_create(':Public') + List.get_or_create(':Inbox') for u in models.User.query.filter((models.User.peered==True)|(models.User.id==settings.USER_ID)): lists += u.lists_json() return { @@ -289,7 +289,7 @@ def removeList(data): } ''' l = models.List.get(data['id']) - if l and l.name != 'Public': + if l and l.name != 'Inbox': l.remove() return {} actions.register(removeList, cache=False) @@ -308,12 +308,10 @@ def addListItems(data): i = Item.get(item_id) i.queue_download() i.update() - elif data['list'] and (data['list'].startswith(':') or - data['list'].endswith(':Public') or - data['list'].enswtih(':')): + elif data['list'] and (data['list'].startswith(':') or data['list'].endswith(':')): l = models.List.get_or_create(data['list']) if l: - if l.name in ('Public', '') and l.user_id != settings.USER_ID: + if l.name in ('Inbox', '') and l.user_id != settings.USER_ID: state.tasks.queue('upload', { 'user': l.user_id, 'items': data['items'] @@ -350,12 +348,11 @@ def sortLists(data): ''' n = 0 logger.debug('sortLists %s', data) - lists = ['Public'] for id in data['ids']: l = models.List.get(id) l.index_ = n n += 1 - if l.type == 'static': + if l.type == 'static' and l.name not in ('', 'Inbox'): lists.append(l.name) state.db.session.add(l) state.db.session.commit() diff --git a/oml/user/models.py b/oml/user/models.py index bc2d072..f90cf89 100644 --- a/oml/user/models.py +++ b/oml/user/models.py @@ -217,7 +217,7 @@ class User(db.Model): Changelog.record(self, 'edititem', item.id, item.meta, _commit=False) lists = [] for l in List.query.filter_by(user_id=self.id, type='static').order_by('index_'): - if l.name: + if l.name and l.name != 'Inbox': lists.append(l.name) Changelog.record(self, 'addlist', l.name, _commit=False) items = [i.id for i in l.get_items().options(load_only('id'))] @@ -300,7 +300,7 @@ class List(db.Model): state.db.session.add(l) state.db.session.commit() if user_id == settings.USER_ID: - if l.type == 'static' and name != '': + if l.type == 'static' and name != '' and name != 'Inbox': add_record('addlist', l.name) return l diff --git a/static/js/folderList.js b/static/js/folderList.js index 44d5a57..323766e 100644 --- a/static/js/folderList.js +++ b/static/js/folderList.js @@ -13,7 +13,7 @@ oml.ui.folderList = function(options) { src: Ox.UI.getImageURL( value == 'libraries' ? 'symbolData' : value == 'library' ? 'symbolUser' - : data.name == 'Public' ? 'symbolPublish' + : data.name == 'Inbox' ? 'symbolPublish' : value == 'static' ? 'symbolClick' : 'symbolFind' ) diff --git a/static/js/folders.js b/static/js/folders.js index b34545d..02c74dd 100644 --- a/static/js/folders.js +++ b/static/js/folders.js @@ -42,7 +42,7 @@ oml.ui.folders = function() { }).indexOf(list.user) : null; return list.id == '' ? oml.$ui.librariesList : Ox.endsWith(list.id, ':') ? oml.$ui.libraryList[index] - : Ox.endsWith(list.id, ':Public') ? oml.$ui.publicList[index] + : Ox.endsWith(list.id, ':Inbox') ? oml.$ui.inboxList[index] : oml.$ui.folderList[index]; } @@ -66,7 +66,7 @@ oml.ui.folders = function() { !list ? 'libraryList' : 'folderList' ][index] - : list == 'Public' ? oml.$ui.publicList[index] + : list == 'Inbox' ? oml.$ui.inboxList[index] : oml.$ui.folderList[index]; $lists.forEach(function($list) { if ($list == $selectedList) { @@ -87,7 +87,7 @@ oml.ui.folders = function() { oml.$ui.folder = []; oml.$ui.libraryList = []; oml.$ui.folderList = []; - oml.$ui.publicList = []; + oml.$ui.inboxList = []; getUsersAndLists(function(users, lists) { @@ -120,10 +120,11 @@ oml.ui.folders = function() { var $content, items = lists.filter(function(list) { return list.user === user.name - && list.type != 'library' && list.name != 'Public'; + && list.type != 'library' && list.name != 'Inbox'; }), libraryId = user.name + ':', - publicId = user.name + ':Public'; + inboxId = user.name + ':Inbox', + offset = 16; userIndex[user.name] = index; @@ -193,7 +194,7 @@ oml.ui.folders = function() { oml.UI.set({find: getFind(data.ids[0])}); }, selectnext: function() { - oml.UI.set({find: getFind(publicId)}); + oml.UI.set({find: getFind(inboxId)}); }, selectprevious: function() { // FIXME: ugly @@ -216,27 +217,29 @@ oml.ui.folders = function() { }) .appendTo($content) ); - - $lists.push( - oml.$ui.publicList[index] = oml.ui.folderList({ - items: lists.filter(function(list) { - return list.user == user.name && list.name == 'Public'; + if (user.name == '') { + $lists.push( + oml.$ui.inboxList[index] = oml.ui.folderList({ + items: lists.filter(function(list) { + return list.user == user.name && list.name == 'Inbox'; + }) }) - }) - .bindEvent({ - select: function(data) { - oml.UI.set({find: getFind(data.ids[0])}); - }, - selectnext: function() { - oml.UI.set({find: getFind(items[0].id)}); - }, - selectprevious: function() { - oml.UI.set({find: getFind(libraryId)}); - } - }) - .appendTo($content) - ); - oml.$ui.publicList[index].$body.css({top: '16px'}); + .bindEvent({ + select: function(data) { + oml.UI.set({find: getFind(data.ids[0])}); + }, + selectnext: function() { + oml.UI.set({find: getFind(items[0].id)}); + }, + selectprevious: function() { + oml.UI.set({find: getFind(libraryId)}); + } + }) + .appendTo($content) + ); + oml.$ui.inboxList[index].$body.css({top: offset + 'px'}); + offset += 16; + } $lists.push( oml.$ui.folderList[index] = oml.ui.folderList({ @@ -258,7 +261,7 @@ oml.ui.folders = function() { } }, 'delete': function(data) { - !index && !Ox.contains(data.ids, ':Public') && oml.ui.deleteListDialog().open(); + !index && !Ox.contains(data.ids, ':Inbox') && oml.ui.deleteListDialog().open(); }, key_control_d: function() { oml.addList(ui._list); @@ -275,7 +278,7 @@ oml.ui.folders = function() { }); }, open: function(data) { - !index && !Ox.contains(data.ids, ':Public') && oml.ui.listDialog().open(); + !index && !Ox.contains(data.ids, ':Inbox') && oml.ui.listDialog().open(); }, select: function(data) { oml.UI.set({find: getFind(data.ids[0])}); @@ -290,14 +293,14 @@ oml.ui.folders = function() { } }, selectprevious: function() { - oml.UI.set({find: getFind(publicId)}); + oml.UI.set({find: getFind(inboxId)}); } }) .css({height: items.length * 16 + 'px'}) .appendTo($content) ); - oml.$ui.folderList[index].$body.css({top: '32px'}); + oml.$ui.folderList[index].$body.css({top: offset + 'px'}); }); @@ -353,7 +356,7 @@ oml.ui.folders = function() { oml.$ui.folder[index].options({title: Ox.encodeHTMLEntities(name)}); oml.getLists(function(lists) { var items = lists.filter(function(list) { - return list.user == name && list.type != 'library' && list.name != 'Public'; + return list.user == name && list.type != 'library' && list.name != 'Inbox'; }), library = lists.filter(function(list) { return list.user == name && list.type == 'library'; diff --git a/static/js/mainMenu.js b/static/js/mainMenu.js index 45280e2..501cc03 100644 --- a/static/js/mainMenu.js +++ b/static/js/mainMenu.js @@ -956,7 +956,7 @@ oml.ui.mainMenu = function() { isLibrary = Ox.endsWith(ui._list, ':'), isList = !isLibraries && !isLibrary, isOwnList = ui._list[0] == ':', - isPublic = ui._list == ':Public'; + isInbox = ui._list == ':Inbox'; if (oml.readOnly) { return { @@ -1020,13 +1020,13 @@ oml.ui.mainMenu = function() { id: 'editlist', title: Ox._('Edit List...'), keyboard: 'return', - disabled: !isList || !isOwnList || isPublic + disabled: !isList || !isOwnList || isInbox }, { id: 'deletelist', title: Ox._('Delete List...'), keyboard: 'delete', - disabled: !isList || !isOwnList || isPublic + disabled: !isList || !isOwnList || isInbox } ]) }; diff --git a/static/js/utils.js b/static/js/utils.js index f64412a..bdbdb27 100644 --- a/static/js/utils.js +++ b/static/js/utils.js @@ -316,7 +316,7 @@ oml.enableDragAndDrop = function($list, canMove) { $list.bindEvent({ draganddropstart: function(data) { - var $lists = oml.$ui.libraryList.concat(oml.$ui.publicList).concat(oml.$ui.folderList); + var $lists = oml.$ui.libraryList.concat(oml.$ui.inboxList).concat(oml.$ui.folderList); drag.action = 'copy'; drag.ids = $list.options('selected'); drag.item = drag.ids.length == 1 @@ -335,9 +335,9 @@ oml.enableDragAndDrop = function($list, canMove) { && drag.source.user != '' && data.user == '' ) || ( - data.type == 'static' - && data.name == 'Public' + data.type == 'library' && drag.source.user == '' + && data.user != '' ), selected: data.id == ui._list }, data); @@ -492,8 +492,12 @@ oml.enableDragAndDrop = function($list, canMove) { text = Ox._('You cannot move books
out of a smart list.'); } } else if (drag.target) { + console.log(drag.target) targetText = drag.target.type == 'libraries' ? Ox._('a library') - : drag.target.type == 'library' ? Ox._('your library') + : drag.target.type == 'library' ? + drag.target.user == '' + ? Ox._('your library') + : Ox._('{0}\'s library', [Ox.encodeHTMLEntities(Ox.truncate(drag.target.user, 32))]) : Ox._('the list "{0}"', [Ox.encodeHTMLEntities(Ox.truncate(drag.target.name, 32))]); if ( ( @@ -507,7 +511,7 @@ oml.enableDragAndDrop = function($list, canMove) { Ox._(itemText[0] == '"' ? '' : 'These ') + itemText, targetText ]); - } else if (drag.target.user != '' && drag.target.name != 'Public') { + } else if (drag.target.user != '' && drag.target.type != 'library') { text = Ox._( 'You can only {0} books
to your own {1}.', [actionText, drag.target.type == 'library' ? 'library' : 'lists'] @@ -992,7 +996,7 @@ oml.resizeListFolders = function() { oml.$ui.libraryList[index] .css({width: width + 'px'}) .resizeColumn('name', columnWidth); - oml.$ui.publicList[index] + oml.$ui.inboxList[index] && oml.$ui.inboxList[index] .css({width: width + 'px'}) .resizeColumn('name', columnWidth); oml.$ui.folderList[index] From d8ce172716aa1fa002723294bc6e00920d038a13 Mon Sep 17 00:00:00 2001 From: j Date: Mon, 28 Jan 2019 22:38:31 +0530 Subject: [PATCH 15/15] folder list height --- static/js/folders.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/static/js/folders.js b/static/js/folders.js index 02c74dd..f4e84a3 100644 --- a/static/js/folders.js +++ b/static/js/folders.js @@ -176,7 +176,8 @@ oml.ui.folders = function() { $content = oml.$ui.folder[index].$content .css({ - height: (2 + items.length) * 16 + 'px' + // user also has inbox + height: ((index ? 1 : 2) + items.length) * 16 + 'px' }); $lists.push( @@ -365,9 +366,9 @@ oml.ui.folders = function() { oml.$ui.libraryList[index].options({ items: library }); - // library + public + lists + // library + inbox + lists oml.$ui.folder[index].$content - .css({height: 16 + 16 + items.length * 16 + 'px'}); + .css({height: (index ? 16 : 32) + items.length * 16 + 'px'}); oml.$ui.folderList[index].options({ items: items })