From 47fe7b1c5324875c7f714a20a471faee8265b9bd Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 22 Sep 2017 19:35:06 +0200 Subject: [PATCH] Drive in sandboxed iframe --- www/common/common-interface.js | 2 +- www/common/cryptpad-common.js | 11 + www/common/fsStore.js | 4 +- www/common/mergeDrive.js | 12 +- www/common/sframe-common-outer.js | 12 + www/common/sframe-common.js | 13 + www/common/sframe-protocol.js | 9 + www/common/userObject.js | 20 +- www/newdrive/app-drive.less | 242 +++++++----- www/newdrive/inner.html | 60 +-- www/newdrive/inner.js | 628 ++++++++++++++++-------------- www/settings/main.js | 2 +- 12 files changed, 573 insertions(+), 442 deletions(-) diff --git a/www/common/common-interface.js b/www/common/common-interface.js index 6ebd2bec0..fc91a655b 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -342,7 +342,7 @@ define([ $ok.click(); }, function () { $cancel.click(); - }); + }, ok); document.body.appendChild(frame); setTimeout(function () { diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 1d169cd87..95ba35f13 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -2236,6 +2236,17 @@ define([ } $iframe.load(w2); //cb); } + }).nThen(function (waitFor) { + if (sessionStorage.migrateAnonDrive) { + var w = waitFor(); + require(['/common/mergeDrive.js'], function (Merge) { + var hash = localStorage.FS_hash; + Merge.anonDriveIntoUser(getStore().getProxy(), hash, function () { + delete sessionStorage.migrateAnonDrive; + w(); + }); + }); + } }).nThen(function () { updateLocalVersion(); common.addTooltips(); diff --git a/www/common/fsStore.js b/www/common/fsStore.js index 5a8e0becb..f24409d8b 100644 --- a/www/common/fsStore.js +++ b/www/common/fsStore.js @@ -4,7 +4,7 @@ define([ '/bower_components/chainpad-crypto/crypto.js?v=0.1.5', '/bower_components/textpatcher/TextPatcher.amd.js', '/common/userObject.js', - '/common/migrate-user-object.js' + '/common/migrate-user-object.js', ], function ($, Listmap, Crypto, TextPatcher, FO, Migrate) { /* This module uses localStorage, which is synchronous, but exposes an @@ -196,11 +196,11 @@ define([ Migrate(proxy, Cryptpad); - //storeObj = proxy; store = initStore(fo, proxy, exp); if (typeof(f) === 'function') { f(void 0, store); } + //storeObj = proxy; var requestLogin = function () { // log out so that you don't go into an endless loop... diff --git a/www/common/mergeDrive.js b/www/common/mergeDrive.js index ce3e0b157..596049475 100644 --- a/www/common/mergeDrive.js +++ b/www/common/mergeDrive.js @@ -83,9 +83,9 @@ define([ }); }; - exp.anonDriveIntoUser = function (proxy, cb) { + exp.anonDriveIntoUser = function (proxyData, fsHash, cb) { // Make sure we have an FS_hash and we don't use it, otherwise just stop the migration and cb - if (!localStorage.FS_hash || !Cryptpad.isLoggedIn()) { + if (!fsHash || !Cryptpad.isLoggedIn()) { if (typeof(cb) === "function") { return void cb(); } } // Get the content of FS_hash and then merge the objects, remove the migration key and cb @@ -102,13 +102,13 @@ define([ return; } if (parsed) { + var proxy = proxyData.proxy; var oldFo = FO.init(parsed.drive, { Cryptpad: Cryptpad }); var onMigrated = function () { oldFo.fixFiles(); - var newData = Cryptpad.getStore().getProxy(); - var newFo = newData.fo; + var newFo = proxyData.fo; var oldRecentPads = parsed.drive[newFo.FILES_DATA]; var newRecentPads = proxy.drive[newFo.FILES_DATA]; var newFiles = newFo.getFiles([newFo.FILES_DATA]); @@ -150,7 +150,7 @@ define([ if (!proxy.FS_hashes || !Array.isArray(proxy.FS_hashes)) { proxy.FS_hashes = []; } - proxy.FS_hashes.push(localStorage.FS_hash); + proxy.FS_hashes.push(fsHash); if (typeof(cb) === "function") { cb(); } }; oldFo.migrate(onMigrated); @@ -158,7 +158,7 @@ define([ } if (typeof(cb) === "function") { cb(); } }; - Crypt.get(localStorage.FS_hash, todo); + Crypt.get(fsHash, todo); }; return exp; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 5aed37271..f9f7d6fae 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -294,6 +294,12 @@ define([ }); }); + sframeChan.on('Q_SESSIONSTORAGE_PUT', function (data, cb) { + sessionStorage[data.key] = data.value; + cb(); + }); + + // Present mode URL sframeChan.on('Q_PRESENT_URL_GET_VALUE', function (data, cb) { var parsed = Cryptpad.parsePadUrl(window.location.href); @@ -385,6 +391,12 @@ define([ } }); + sframeChan.on('EV_OPEN_URL', function (url) { + if (url) { + window.open(url); + } + }); + sframeChan.on('Q_TAGS_GET', function (data, cb) { Cryptpad.getPadTags(null, function (err, data) { cb({ diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index b658bf959..3ca91d7c0 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -210,6 +210,15 @@ define([ }, cb); }; + funcs.sessionStorage = { + put: function (key, value, cb) { + ctx.sframeChan.query('Q_SESSIONSTORAGE_PUT', { + key: key, + value: value + }, cb); + } + }; + funcs.isStrongestStored = function () { var data = ctx.metadataMgr.getPrivateData(); if (data.availableHashes.fileHash) { return true; } @@ -221,6 +230,9 @@ define([ ctx.sframeChan.query('Q_SETTINGS_SET_DISPLAY_NAME', name, cb); }; + funcs.mergeAnonDrive = function (cb) { + ctx.sframeChan.query('Q_MERGE_ANON_DRIVE', null, cb); + }; // Friends var pendingFriends = []; funcs.getPendingFriends = function () { @@ -271,6 +283,7 @@ define([ }; */ funcs.gotoURL = function (url) { ctx.sframeChan.event('EV_GOTO_URL', url); }; + funcs.openURL = function (url) { ctx.sframeChan.event('EV_OPEN_URL', url); }; funcs.whenRealtimeSyncs = evRealtimeSynced.reg; diff --git a/www/common/sframe-protocol.js b/www/common/sframe-protocol.js index 2c97c6c45..e4a6d7c02 100644 --- a/www/common/sframe-protocol.js +++ b/www/common/sframe-protocol.js @@ -126,6 +126,9 @@ define({ // Make the browser window navigate to a given URL, if no URL is passed then it will reload. 'EV_GOTO_URL': true, + // Make the parent window open a given URL in a new tab. It allows us to keep sessionStorage + // form the parent window. + 'EV_OPEN_URL': true, // Present mode URL 'Q_PRESENT_URL_GET_VALUE': true, @@ -136,9 +139,15 @@ define({ 'EV_CACHE_PUT': true, // Put one or more entries to the localStore which will go in localStorage. 'EV_LOCALSTORE_PUT': true, + // Put one entry in the parent sessionStorage + 'Q_SESSIONSTORAGE_PUT': true, // Set and get the tags using the tag prompt button 'Q_TAGS_GET': true, 'EV_TAGS_SET': true, + // Merge the anonymous drive (FS_hash) into the current logged in user's drive, to keep the pads + // in the drive at registration. + 'Q_MERGE_ANON_DRIVE': true, + }); diff --git a/www/common/userObject.js b/www/common/userObject.js index 6e3ebf936..0150b1528 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -18,6 +18,7 @@ define([ var exp = {}; var Cryptpad = config.Cryptpad; var Messages = Cryptpad.Messages; + var loggedIn = config.loggedIn || Cryptpad.isLoggedIn(); var FILES_DATA = module.FILES_DATA = exp.FILES_DATA = Cryptpad.storageKey; var OLD_FILES_DATA = module.OLD_FILES_DATA = exp.OLD_FILES_DATA = Cryptpad.oldStorageKey; @@ -481,14 +482,14 @@ define([ // FILES DATA exp.pushData = function (data, cb) { + // TODO: can only be called from outside atm if (typeof cb !== "function") { cb = function () {}; } var todo = function () { var id = Cryptpad.createRandomInteger(); files[FILES_DATA][id] = data; cb(null, id); }; - // TODO - if (!Cryptpad.isLoggedIn() || !AppConfig.enablePinning || config.testMode) { + if (!loggedIn || !AppConfig.enablePinning || config.testMode) { return void todo(); } Cryptpad.pinPads([Cryptpad.hrefToHexChannelId(data.href)], function (e) { @@ -586,8 +587,7 @@ define([ // ADD var add = exp.add = function (id, path) { - // TODO - if (!Cryptpad.isLoggedIn() && !config.testMode) { return; } + if (!loggedIn && !config.testMode) { return; } var data = files[FILES_DATA][id]; if (!data || typeof(data) !== "object") { return; } var newPath = path, parentEl; @@ -626,8 +626,7 @@ define([ exp.forget = function (href) { var id = getIdFromHref(href); if (!id) { return; } - // TODO - if (!Cryptpad.isLoggedIn() && !config.testMode) { + if (!loggedIn && !config.testMode) { // delete permanently exp.removePadAttribute(href); spliceFileData(id); @@ -656,8 +655,7 @@ define([ }; var checkDeletedFiles = function () { // Nothing in OLD_FILES_DATA for workgroups - // TODO - if (workgroup || (!Cryptpad.isLoggedIn() && !config.testMode)) { return; } + if (workgroup || (!loggedIn && !config.testMode)) { return; } var filesList = getFiles([ROOT, 'hrefArray', TRASH]); getFiles([FILES_DATA]).forEach(function (id) { @@ -684,8 +682,7 @@ define([ var trashPaths = paths.filter(function(x) { return isPathIn(x, [TRASH]); }); var allFilesPaths = paths.filter(function(x) { return isPathIn(x, [FILES_DATA]); }); - // TODO - if (!Cryptpad.isLoggedIn() && !config.testMode) { + if (!loggedIn && !config.testMode) { allFilesPaths.forEach(function (path) { var el = find(path); if (!el) { return; } @@ -1086,8 +1083,7 @@ define([ migrateAttributes(el, id, parsed); - // TODO - if ((Cryptpad.isLoggedIn() || config.testMode) && rootFiles.indexOf(id) === -1) { + if ((loggedIn || config.testMode) && rootFiles.indexOf(id) === -1) { debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", id, el); var newName = Cryptpad.createChannelId(); root[newName] = id; diff --git a/www/newdrive/app-drive.less b/www/newdrive/app-drive.less index f1d2e011a..dd9f84ed4 100644 --- a/www/newdrive/app-drive.less +++ b/www/newdrive/app-drive.less @@ -101,12 +101,66 @@ min-height: auto; } } -img.icon { +/* local mixins */ +.fileIcon { + li { + display: inline-block; + margin: 10px 10px; + width: 140px; + height: 140px; + text-align: center; + vertical-align: top; + overflow: hidden; + text-overflow: ellipsis; + padding-top: 5px; + padding-bottom: 5px; + + &:not(.cp-app-drive-element-selected):not(.cp-app-drive-element-selected-tmp) { + border: 1px solid #CCC; + } + .cp-app-drive-element-name { + width: 100%; + height: 48px; + margin: 8px 0; + display: inline-block; + //align-items: center; + //justify-content: center; + overflow: hidden; + //text-overflow: ellipsis; + word-wrap: break-word; + } + .cp-app-drive-element-truncated { + display: block; + position: absolute; + bottom: 0px; + left: 0; right: 0; + text-align: center; + } + img.cp-app-drive-content-icon { + height: 48px; + max-height: none; + max-width: none; + margin: 8px 0; + } + .fa { + display: block; + margin: auto; + font-size: 48px; + margin: 8px 0; + text-align: center; + &.listonly { + display: none; + } + } + } +} + +img.cp-app-drive-icon { max-width: 20px; max-height: 16px; } -.unselectable { +.cp-unselectable { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; @@ -115,7 +169,7 @@ img.icon { user-select: none; } -.app-container { +.cp-app-drive-container { flex: 1; overflow: auto; width: 100%; @@ -123,18 +177,18 @@ img.icon { flex-flow: row; @media screen and (max-width: @size-mobile) { display: block; - #driveToolbar { + #cp-app-drive-toolbar { .path .element { display: none; } } - #tree { + #cp-app-drive-tree { resize: none; width: 100%; max-width: unset; max-height: unset; border-bottom: 1px solid @toolbar-border-col; - .category { + .cp-app-drive-tree-category { margin-top: 0.5em; } } @@ -171,7 +225,7 @@ li { user-select: none; } -.contextMenu { +.cp-app-drive-context { display: none; position: absolute; z-index: 500; @@ -184,12 +238,12 @@ li { } } -.droppable { +.cp-app-drive-element-droppable { background-color: #FE9A2E; color: #222; } -.selected { +.cp-app-drive-element-selected { background: #666 !important; color: #eee; margin: -1px; @@ -198,7 +252,7 @@ li { } } -.selectedTmp { +.cp-app-drive-element-selected-tmp { border: 1px dotted #bbb; background: #AAA; color: #ddd; @@ -218,7 +272,7 @@ span { /* TREE */ -#tree { +#cp-app-drive-tree { font-size: @main-font-size; //border-right: 1px solid #ccc; box-sizing: border-box; @@ -234,23 +288,23 @@ span { display: flex; flex-flow: column; max-height: 100%; - .categories-container { + .cp-app-drive-tree-categories-container { flex: 1; max-width: 500px; overflow: auto; } - img.icon { + img.cp-app-drive-icon { margin-bottom: 3px; margin-left: -2px; } - .docTree { + .cp-app-drive-tree-docs { margin-top: 20px; //padding: 0 0 0 20px; padding: 0; cursor: auto; &li, li { padding: 0; - &.collapsed ul { + &.cp-app-drive-element-collapsed ul { display: none; } input { @@ -259,7 +313,7 @@ span { border: 0; color: lighten(@tree-fg, 40%); } - & > span.element-row { + & > span.cp-app-drive-element-row { overflow: hidden; text-overflow: ellipsis; //min-width: ~"calc(100% + 5px)"; @@ -272,23 +326,23 @@ span { margin-left: -5px; padding-left: 5px; } - & > span.element-row:not(.selected):not(.active):hover { + & > span.cp-app-drive-element-row:not(.cp-app-drive-element-selected):not(.cp-app-drive-element-active):hover { //background-color: @drive-hover; } } } - span.element { + span.cp-app-drive-element { cursor: pointer; } - /*.active { - &:not(.selected):not(.droppable) { + /*.cp-app-drive-element-active { + &:not(.cp-app-drive-element-selected):not(.cp-app-drive-element-droppable) { background-color: darken(@drive-hover, 15%); } }*/ - .category { + .cp-app-drive-tree-category { margin: 0; margin-top: 15px; - .root { + .cp-app-drive-tree-root { &> .fa { min-width: 30px; cursor: pointer; @@ -296,7 +350,7 @@ span { } li { padding: 0; - .element-row { + .cp-app-drive-element-row { display: block; padding-left: 20px; .leftsideCategory(); @@ -307,16 +361,13 @@ span { } } } - .category:last-child { + .cp-app-drive-tree-category:last-child { margin-bottom: 20px; } - #allfilesTree { - margin-top: 0; - } .limit-container { margin-top: 0; } - #searchContainer { + #cp-app-drive-tree-search { text-align: center; padding: 0; position: relative; @@ -338,14 +389,14 @@ span { outline-width: 0px; } } - .searchIcon { + .cp-app-drive-tree-search-con { color: @toolbar-drive-color; position: absolute; left: 20px; // TODO align with drive categories top: 8px; } } - .fa.expcol { + .fa.cp-app-drive-icon-expcol { margin-left: -10px; font-size: 14px; position: absolute; @@ -363,17 +414,17 @@ span { top: -1px; } } - .docTree { - .root > .element-row > .expcol { + .cp-app-drive-tree-docs { + .cp-app-drive-tree-root > .cp-app-drive-element-row > .cp-app-drive-icon-expcol { position: relative; top:0; left: -10px; } - .root > .element-row > .folder { + .cp-app-drive-tree-root > .cp-app-drive-element-row > .cp-app-drive-icon-folder { margin-left: -5px; } - .root { - &> .element-row { + .cp-app-drive-tree-root { + &> .cp-app-drive-element-row { padding-left: 20px; } &> ul { @@ -383,7 +434,7 @@ span { } // Expand/collapse lines - .docTree ul { + .cp-app-drive-tree-docs ul { margin: 0px 0px 0px 10px; list-style: none; padding-left: 10px; @@ -409,7 +460,7 @@ span { border-left: 1px solid @tree-lines-col; height: 100%; } - &.root { + &.cp-app-drive-tree-root { margin: 0px 0px 0px -10px; &:before { display: none; @@ -426,7 +477,7 @@ span { } /* CONTENT */ -#rightCol { +#cp-app-drive-content-container { display: flex; flex-flow: column; flex: 1; @@ -434,7 +485,7 @@ span { // https://stackoverflow.com/questions/38223879/white-space-nowrap-breaks-flexbox-layout min-width: 0; } -#content { +#cp-app-drive-content { box-sizing: border-box; background: @content-bg; color: @content-fg; @@ -443,20 +494,20 @@ span { display: flex; flex-flow: column; position: relative; - .selectBox { + .cp-app-drive-content-select-box { display: none; background-color: rgba(100, 100, 100, 0.7); position: absolute; z-index: 50; } - &.readonly { + &.cp-app-drive-readonly { background: @content-bg-ro; } h1 { padding-left: 10px; margin-top: 10px; } - .info-box { + .cp-app-drive-content-info-box { line-height: 2em; padding: 0.25em 0.75em; margin: 1em; @@ -466,28 +517,26 @@ span { float: right; margin-top: 0.5em; } - &.noclose { - } } li { cursor: default; - &:not(.header) { + &:not(.cp-app-drive-element-header) { *:not(input) { /*pointer-events: none;*/ } &:hover { - &:not(.selected, .selectedTmp) { + &:not(.-cp-app-drive-element-selected, .cp-app-drive-element-selected-tmp) { background-color: @drive-hover; } - .name { + .cp-app-drive-element-name { /*text-decoration: underline;*/ } } } } - #folderContent { + #cp-app-drive-content-folder { li { - &.searchResult { + &.cp-app-drive-search-result { border-bottom: 1px solid @info-box-border; display: block; &:hover { @@ -495,13 +544,13 @@ span { } table { width: 100%; - .label2 { + .cp-app-drive-search-label2 { width: 150px; font-size: 15px; text-align: right; padding-right: 15px; } - .openDir { + .cp-app-drive-search-opendir { a { cursor: pointer; color: #41b7d8; @@ -511,25 +560,25 @@ span { } } } - .path { + .cp-app-drive-search-path { font-style: italic; direction: rtl; - .element { + .cp-app-drive-path-element { display: inline-block; margin-right: 5px; } } - .title { + .cp-app-drive-search-title { font-weight: bold; cursor: pointer; &:hover { background-color: @drive-hover; } } - .col2 { + .cp-app-drive-search-col2 { width: 250px; } - td.icon { + td.cp-app-drive-search-icon { width: 50px; font-size: 40px; } @@ -537,21 +586,21 @@ span { } } } - .element { - .truncated { display: none; } + .cp-app-drive-element { + .cp-app-drive-element-truncated { display: none; } } - div.grid { + div.cp-app-drive-content-grid { padding: 20px; .fileIcon; li { - &.element { + &.cp-app-drive-element { position: relative; } input { width: 100%; margin-top: 5px; } - .state { + .cp-app-drive-element-state { position: absolute; top: 3px; right: 3px; @@ -561,10 +610,10 @@ span { } } } - .listElement { + .cp-app-drive-element-list { display: none; } - .addpad { + .cp-app-drive-new-ghost { cursor: pointer; opacity: 0.5; padding: 0; @@ -580,8 +629,8 @@ span { } } - .list { - .grid-element { + .cp-app-drive-content-list { + .cp-app-drive-element-grid { display: none; } // Make it act as a table! @@ -597,11 +646,11 @@ span { padding: 0 5px; display: table-cell; } - &:not(.header) { + &:not(.cp-app-drive-element-header) { height: @toolbar-line-height; line-height: @toolbar-line-height; } - &.header { + &.cp-app-drive-element-header { cursor: default; color: @table-header-fg; span { @@ -614,10 +663,10 @@ span { } &> span { padding: 15px 5px; - &.active { + &.cp-app-drive-sort-active { font-weight: bold; } - &.clickable { + &.cp-app-drive-sort-clickable { cursor: pointer; &:hover { background: @table-header-bg; @@ -626,29 +675,29 @@ span { } } } - .element { + .cp-app-drive-element { span { overflow: hidden; white-space: nowrap; box-sizing: border-box; - &.state { + &.cp-app-drive-element-state { .fa:not(:last-child) { margin-right: 5px; } } - &.icon, &.state { + &.cp-app-drive-content-icon, &.cp-app-drive-element-state { width: 30px; } - &.type, &.atime, &.ctime { + &.cp-app-drive-element-type, &.cp-app-drive-element-atime, &.cp-app-drive-element-ctime { width: 175px; } - &.title { + &.cp-app-drive-element-title { width: 250px; @media screen and (max-width: 1200px) { display: none; } } - &.folders, &.files { + &.cp-app-drive-element-folders, &.cp-app-drive-element-files { width: 150px; } } @@ -656,23 +705,15 @@ span { } } -.parentFolder { - cursor: pointer; - margin-left: 10px; - &:hover { - text-decoration: underline; - } -} - -#folderContent { +#cp-app-drive-content-folder { padding-right: 10px; flex: 1; } -#addPadDialog.cp-modal-container { +#cp-app-drive-new-ghost-dialog.cp-modal-container { .fileIcon; - li:not(.selected):hover { + li:not(.cp-app-drive-element-selected):hover { border: 1px solid white; } .cp-modal { @@ -690,7 +731,7 @@ span { justify-content: center; align-content: center; overflow-y: auto; - .uploadFile { + .cp-app-drive-new-upload { break-after: always; page-break-after: always; } @@ -712,7 +753,7 @@ span { .fa { font-size: 32px; } - .name { + .cp-app-drive-new-name { height: auto; } } @@ -724,7 +765,7 @@ span { /* Toolbar */ -#driveToolbar { +#cp-app-drive-toolbar { background: lighten(@toolbar-drive-bg, 8%); color: @toolbar-drive-color; //height: 30px; @@ -744,11 +785,6 @@ span { } } - .newPadContainer { - display: inline-block; - height: 100%; - } - .history { float: right; .cp-toolbar-drawer-element { @@ -756,7 +792,7 @@ span { } } - .rightside, .leftside { + .cp-app-drive-toolbar-rightside, .cp-app-drive-toolbar-leftside { display: inline-block; margin: 0; padding: 0; @@ -782,23 +818,23 @@ span { &:hover { background: @toolbar-drive-bg; } - &.active { + &.cp-app-drive-toolbar-active { display: none; } } } - .rightside { + .cp-app-drive-toolbar-rightside { float: right; & > * { float: right; } - #contextButtonsContainer { + #cp-app-drive-toolbar-contextbuttons { display: inline-block; height: 100%; } padding-left: 10px; } - .leftside { + .cp-app-drive-toolbar-leftside { & > span { height: 100%; margin: 0; @@ -839,7 +875,7 @@ span { margin-right: 2px; } - .path { + .cp-app-drive-path { flex: 1; width: 100%; height: @toolbar-line-height; @@ -851,7 +887,7 @@ span { direction: rtl; max-width: 100%; text-align: left; - .element { + .cp-app-drive-path-element { display: inline-block; height: @toolbar-line-height; line-height: @toolbar-line-height; @@ -862,10 +898,10 @@ span { color: @toolbar-drive-color; box-sizing: border-box; transition: all 0.15s; - &.separator { + &.cp-app-drive-path-separator { color: #ccc; } - &.clickable { + &.cp-app-drive-path-lickable { cursor: pointer; &:hover { background: darken(@toolbar-drive-bg, 15%); diff --git a/www/newdrive/inner.html b/www/newdrive/inner.html index efd242d59..afba5fccc 100644 --- a/www/newdrive/inner.html +++ b/www/newdrive/inner.html @@ -10,51 +10,51 @@
-
-
+
+
-
-
-
+
+
+
-