From 07096a9e2536a958ceaf8c412aa3ec4c9d41ce83 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 19 Feb 2025 18:35:32 +0100 Subject: [PATCH 01/14] Reduce server memory usage when accessing document history --- lib/hk-util.js | 46 ++++++++++++++++++++++++++++++++++++---- lib/storage/file.js | 2 +- lib/workers/db-worker.js | 44 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/lib/hk-util.js b/lib/hk-util.js index e9d177968..4ac1ffdd7 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -755,15 +755,16 @@ const handleGetHistoryRange = function (Env, Server, seq, userId, parsed) { var channelName = parsed[1]; var map = parsed[2]; const HISTORY_KEEPER_ID = Env.id; + const store = Env.store; if (!(map && typeof(map) === 'object')) { return void Server.send(userId, [seq, 'ERROR', 'INVALID_ARGS', HISTORY_KEEPER_ID]); } - var oldestKnownHash = map.from; - var untilHash = map.to; - var desiredMessages = map.count; - var desiredCheckpoint = map.cpCount; + var oldestKnownHash = map.from; // last known hash + var untilHash = map.to; // oldest hash (unknown), start point if defined + var desiredMessages = map.count; // nb messages before lkh + var desiredCheckpoint = map.cpCount; // nb cp before lkh var txid = map.txid; if (typeof(desiredMessages) !== 'number' && typeof(desiredCheckpoint) !== 'number' && !untilHash) { return void Server.send(userId, [seq, 'ERROR', 'UNSPECIFIED_COUNT', HISTORY_KEEPER_ID]); @@ -774,6 +775,43 @@ const handleGetHistoryRange = function (Env, Server, seq, userId, parsed) { } Server.send(userId, [seq, 'ACK']); + + if (untilHash) { + // Get all messages between untilHash (oldest but unknown) + // and oldestKnownHesh (last known hash) (or until the end if undefined) + // Messages can be streamed since we instantly know the start point + let found = false; + store.readMessagesBin(channelName, 0, (msgObj, readMore, abort) => { + const parsed = tryParse(Env, msgObj.buff.toString('utf8')); + if (!parsed) { return void readMore(); } + if (isMetadataMessage(parsed)) { return void readMore(); } + const content = parsed[4]; + if (typeof(content) !== 'string') { return void readMore(); } + + const hash = getHash(content); + if (hash === untilHash) { found = true; } + let then = hash === oldestKnownHash ? abort : readMore; + if (found) { + Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, + JSON.stringify(['HISTORY_RANGE', txid, parsed])], then); + } + return void readMore(); + }, function (err, reason) { + if (err) { + Env.Log.error("HK_GET_OLDER_HISTORY", channelName, err, reason); + Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, + JSON.stringify(['HISTORY_RANGE_ERROR', txid, err]) + ]); + return; + } + Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, + JSON.stringify(['HISTORY_RANGE_END', txid, channelName]) + ]); + }); + return; + } + // If desiredCp or desiredMsg are defined, we can't stream and must + // get a list of messages to send from a worker Env.getOlderHistory(channelName, oldestKnownHash, untilHash, desiredMessages, desiredCheckpoint, function (err, toSend) { if (err && err.code !== 'ENOENT') { Env.Log.error("HK_GET_OLDER_HISTORY", err); diff --git a/lib/storage/file.js b/lib/storage/file.js index ff57d922b..4ea2284f2 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -366,7 +366,7 @@ var readMessages = function (path, msgHandler, _cb) { return readFileBin(stream, function (msgObj, readMore) { collector.keepAlive(); msgHandler(msgObj.buff.toString('utf8')); - readMore(); + setTimeout(readMore); }, function (err) { cb(err); }); diff --git a/lib/workers/db-worker.js b/lib/workers/db-worker.js index 64376236f..372e026dc 100644 --- a/lib/workers/db-worker.js +++ b/lib/workers/db-worker.js @@ -40,7 +40,7 @@ Logger.levels.forEach(function (level) { }; }); -const HISTORY_SIZE_LIMIT = 1024 * 1024 * 1024; // XXX 1GB +//const HISTORY_SIZE_LIMIT = 1024 * 1024 * 1024; // XXX 1GB var DETAIL = 1000; var round = function (n) { @@ -384,11 +384,50 @@ const getFileSize = function (data, cb) { const getOlderHistory = function (data, cb) { const oldestKnownHash = data.hash; - const untilHash = data.toHash; const channelName = data.channel; const desiredMessages = data.desiredMessages; const desiredCheckpoint = data.desiredCheckpoint; + let messages = []; + store.readMessagesBin(channelName, 0, (msgObj, readMore, abort) => { + const parsed = HK.tryParse(Env, msgObj.buff.toString('utf8')); + if (!parsed) { return void readMore(); } + if (HK.isMetadataMessage(parsed)) { return void readMore(); } + const content = parsed[4]; + if (typeof(content) !== 'string') { return void readMore(); } + const hash = HK.getHash(content); + + messages.push(parsed); + + // "X" messages before oldestKnownHash + if (typeof (desiredMessages) === "number") { + messages = messages.slice(-desiredMessages); + if (hash === oldestKnownHash) { return void abort(); } + return void readMore(); + } + + // "X" checkpoints before oldestKnownHash + if (hash === oldestKnownHash) { return void abort(); } + if (/^cp\|/.test(content)) { // clean whenever we push a cp + let foundCp = 0; + const idx = messages.findLastIndex(parsed => { + let isCp = /^cp\|/.test(parsed[4]); + if (!isCp) { return; } + foundCp++; + return foundCp >= desiredCheckpoint; + }); + if (idx > 0) { + messages = messages.slice(idx); + } + } + readMore(); + }, function (err, reason) { + if (err) { return void cb(err, reason); } + cb(void 0, messages); + }); + + /* + const untilHash = data.toHash; var next = () => { var messages = []; var found = false; @@ -444,6 +483,7 @@ const getOlderHistory = function (data, cb) { } next(); }); + */ }; const getPinState = function (data, cb) { From a7c8d5cec5e810b5b728783e7e7519a3f60bc743 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 24 Feb 2025 17:04:33 +0100 Subject: [PATCH 02/14] Restart db-workers after a given number of tasks --- lib/workers/index.js | 79 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/lib/workers/index.js b/lib/workers/index.js index 5bdbd5d7a..2fe5be544 100644 --- a/lib/workers/index.js +++ b/lib/workers/index.js @@ -52,16 +52,20 @@ Workers.initialize = function (Env, config, _cb) { //return Object.keys(workers[index].tasks || {}).length; }; +// const WORKER_TASK_LIMIT = 100000; + const WORKER_TASK_LIMIT = 100; // XXX + var workerOffset = -1; var queue = []; - var getAvailableWorkerIndex = function () { + var getAvailableWorkerIndex = function (isQueue) { // If there is already a backlog of tasks you can avoid some work -// by going to the end of the line - if (queue.length) { return -1; } +// by going to the end of the line (unless we're trying to +// empty the queue) + if (queue.length && !isQueue) { return -1; } var L = workers.length; if (L === 0) { - Log.error('NO_WORKERS_AVAILABLE', { + Log.warn('NO_WORKERS_AVAILABLE', { queue: queue.length, }); return -1; @@ -93,7 +97,7 @@ Workers.initialize = function (Env, config, _cb) { }; var drained = true; - var sendCommand = function (msg, _cb, opt) { + var sendCommand = function (msg, _cb, opt, isQueue) { if (!_cb) { return void Log.error('WORKER_COMMAND_MISSING_CB', { msg: msg, @@ -102,7 +106,7 @@ Workers.initialize = function (Env, config, _cb) { } opt = opt || {}; - var index = getAvailableWorkerIndex(); + var index = getAvailableWorkerIndex(isQueue); var state = workers[index]; // if there is no worker available: @@ -114,7 +118,7 @@ Workers.initialize = function (Env, config, _cb) { }); if (drained) { drained = false; - Log.error('WORKER_QUEUE_BACKLOG', { + Log.warn('WORKER_QUEUE_BACKLOG', { workers: workers.length, }); } @@ -134,6 +138,7 @@ Workers.initialize = function (Env, config, _cb) { // an upper bound on the amount of parallelism for any given worker. // if you run out of slots then the worker locks up. delete state.tasks[txid]; + state.checkTasks(); }))); if (!msg) { @@ -164,6 +169,12 @@ Workers.initialize = function (Env, config, _cb) { msg._cb = _cb; msg._opt = opt; }); + + state.count++; + if (state.count > WORKER_TASK_LIMIT) { + // Remove from list and spawn new one + if (state.replaceWorker) { state.replaceWorker(); } + } }; const pluginsResponses = {}; @@ -207,6 +218,8 @@ Workers.initialize = function (Env, config, _cb) { if (!res.txid) { return; } response.handle(res.txid, [res.error, res.value]); delete state.tasks[res.txid]; + state.checkTasks(); + if (!queue.length) { if (!drained) { drained = true; @@ -234,7 +247,7 @@ Workers.initialize = function (Env, config, _cb) { to the back because the following msg took its place. OR, in an even worse scenario, we cycle through the queue but don't run anything. */ - sendCommand(nextMsg.msg, nextMsg.cb); + sendCommand(nextMsg.msg, nextMsg.cb, {}, true); }; const initWorker = function (worker, cb) { @@ -243,13 +256,60 @@ Workers.initialize = function (Env, config, _cb) { const state = { worker: worker, tasks: {}, + count: Math.floor(Math.random()*(WORKER_TASK_LIMIT/10)), pid: worker.pid, // store the child process's id in an easily accessible location }; + state.replaceWorker = () => { + let index = workers.indexOf(state); + if (index === -1) { return; } + // Remove old + workers.splice(index, 1); + // Create new + state.complete = true; + const w = fork(DB_PATH); + Log.debug('WORKER_REPLACE_START', { + from: state.worker.pid, + to: w.pid + }); + initWorker(w, function (err) { + if (err) { + throw new Error(err); + } + }); + }; + + // If we've reached the limit, kill the worker once + // all the tasks are complete or timed out + state.checkTasks = () => { + // Check limit + if (!state.complete || !state.worker) { return; } + // Check remaining tasks + if (Object.keys(state.tasks).length) { return; } + // Kill + Log.debug('WORKER_KILL', { + worker: state.worker.pid, + count: state.count + }); + delete state.worker; + worker.kill(); + } + response.expect(txid, function (err) { if (err) { return void cb(err); } workers.push(state); cb(void 0, state); + // We just pushed a new worker, available to receive + // a task, so we can empty the queue if necessary + if (queue.length) { + const nextMsg = queue.shift(); + if (!nextMsg || !nextMsg.msg) { + return Log.error('WORKER_QUEUE_EMPTY_MESSAGE', { + item: nextMsg, + }); + } + sendCommand(nextMsg.msg, nextMsg.cb, {}, true); + } }, 15000); worker.send({ @@ -296,18 +356,21 @@ Workers.initialize = function (Env, config, _cb) { }); worker.on('exit', function () { + if (!state.worker) { return; } // Manually killed substituteWorker(); Env.Log.error("DB_WORKER_EXIT", { pid: state.pid, }); }); worker.on('close', function () { + if (!state.worker) { return; } // Manually killed substituteWorker(); Env.Log.error("DB_WORKER_CLOSE", { pid: state.pid, }); }); worker.on('error', function (err) { + if (!state.worker) { return; } // Manually killed substituteWorker(); Env.Log.error("DB_WORKER_ERROR", { pid: state.pid, From 39add4c14e78ba82fd32469f20e0cc795cd1ad94 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 26 Feb 2025 18:36:41 +0100 Subject: [PATCH 03/14] Update worker limit --- lib/workers/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/workers/index.js b/lib/workers/index.js index 2fe5be544..55cf49fcc 100644 --- a/lib/workers/index.js +++ b/lib/workers/index.js @@ -52,8 +52,8 @@ Workers.initialize = function (Env, config, _cb) { //return Object.keys(workers[index].tasks || {}).length; }; -// const WORKER_TASK_LIMIT = 100000; - const WORKER_TASK_LIMIT = 100; // XXX + const WORKER_TASK_LIMIT = 1000; // XXX + //const WORKER_TASK_LIMIT = 100; // XXX var workerOffset = -1; var queue = []; From 912b178a720929b1e350c92f6f3156cecbe2e2a2 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 27 Feb 2025 16:23:50 +0100 Subject: [PATCH 04/14] Update log level of db-worker replacement --- lib/workers/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/workers/index.js b/lib/workers/index.js index 55cf49fcc..618b61d3b 100644 --- a/lib/workers/index.js +++ b/lib/workers/index.js @@ -131,7 +131,7 @@ Workers.initialize = function (Env, config, _cb) { var cb = Util.once(Util.mkAsync(Util.both(_cb, function (err /*, value */) { incrementTime(msg && msg.command, start); if (err !== 'TIMEOUT') { return; } - Log.debug("WORKER_TIMEOUT_CAUSE", msg); + Log.warn("WORKER_TIMEOUT_CAUSE", msg); // in the event of a timeout the user will receive an error // but the state used to resend a query in the event of a worker crash // won't be cleared. This also leaks a slot that could be used to keep @@ -268,7 +268,7 @@ Workers.initialize = function (Env, config, _cb) { // Create new state.complete = true; const w = fork(DB_PATH); - Log.debug('WORKER_REPLACE_START', { + Log.info('WORKER_REPLACE_START', { from: state.worker.pid, to: w.pid }); @@ -287,7 +287,7 @@ Workers.initialize = function (Env, config, _cb) { // Check remaining tasks if (Object.keys(state.tasks).length) { return; } // Kill - Log.debug('WORKER_KILL', { + Log.info('WORKER_KILL', { worker: state.worker.pid, count: state.count }); From 5b56b8da93d9ae722010e3d85d07d60c2f9e8731 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 27 Feb 2025 16:24:47 +0100 Subject: [PATCH 05/14] lint compliance --- lib/workers/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/workers/index.js b/lib/workers/index.js index 618b61d3b..a2ed74bbd 100644 --- a/lib/workers/index.js +++ b/lib/workers/index.js @@ -293,7 +293,7 @@ Workers.initialize = function (Env, config, _cb) { }); delete state.worker; worker.kill(); - } + }; response.expect(txid, function (err) { if (err) { return void cb(err); } From 70881e2dff4153426ddc8c4fb425cf0c30ae2d3f Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 3 Mar 2025 14:17:47 +0100 Subject: [PATCH 06/14] Fix history range issue --- lib/hk-util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/hk-util.js b/lib/hk-util.js index 4ac1ffdd7..0a68f9f20 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -789,7 +789,7 @@ const handleGetHistoryRange = function (Env, Server, seq, userId, parsed) { if (typeof(content) !== 'string') { return void readMore(); } const hash = getHash(content); - if (hash === untilHash) { found = true; } + if (hash === untilHash || untilHash === 'NONE') { found = true; } let then = hash === oldestKnownHash ? abort : readMore; if (found) { Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, From d2078c14440aea96388dff025f9175f88cf41a77 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 6 Mar 2025 16:23:45 +0100 Subject: [PATCH 07/14] Fix tags UI issues in kanban #1808 --- customize.dist/src/less2/include/forms.less | 10 ++++ www/kanban/app-kanban.less | 22 +++++--- www/kanban/inner.js | 58 ++++----------------- 3 files changed, 35 insertions(+), 55 deletions(-) diff --git a/customize.dist/src/less2/include/forms.less b/customize.dist/src/less2/include/forms.less index 3c0b206db..0d00204f0 100644 --- a/customize.dist/src/less2/include/forms.less +++ b/customize.dist/src/less2/include/forms.less @@ -199,6 +199,16 @@ background-color: @cp_buttons-default; } } + &.btn-default-alt { + border-color: @cp_buttons-default; + color: @cp_buttons-default; + background-color: @cp_buttons-default-color; + &:hover, &:not(:disabled):active, &:focus { + border-color: @cp_buttons-default-color; + color: @cp_buttons-default-color; + background-color: @cp_toolbar-fade3; + } + } &.danger, &.btn-danger { background-color: @cp_buttons-red; diff --git a/www/kanban/app-kanban.less b/www/kanban/app-kanban.less index 86e1052b5..ef0fc7fd8 100644 --- a/www/kanban/app-kanban.less +++ b/www/kanban/app-kanban.less @@ -145,6 +145,9 @@ margin-bottom: 15px; } + .cp-kanban-toggle-tags { + margin-right: 0.5rem; + } #cp-kanban-edit-tags { .tokenfield { margin: 0; @@ -152,10 +155,6 @@ } margin-bottom: 15px; } - .kanban-tag-btn-toggle { - margin-top: 10px; - margin-left: 10px - } #cp-app-kanban-container { flex: 1; display: flex; @@ -430,12 +429,10 @@ justify-content: space-between; position: relative; min-height: 50px; + align-items: center; .cp-kanban-filterTags { @media (min-width: 505px) { display: inline-flex; - .kanban-tag-btn-toggle { - margin-right: 10px; - } } align-items: center; flex: 1; @@ -571,7 +568,7 @@ display: flex; min-height: 0; .kanban-container { - padding: 30px 5px; + padding: 0px 5px; flex: 1; display: flex; max-height: 100%; @@ -700,6 +697,15 @@ } } + @media (pointer: none), (pointer:coarse) { + .kanban-container-outer { + .kanban-container { + padding: 30px 5px; + } + } + } + + &.cp-app-readonly { .kanban-item, .kanban-title-board { cursor: default !important; diff --git a/www/kanban/inner.js b/www/kanban/inner.js index 947092c59..502da34ad 100644 --- a/www/kanban/inner.js +++ b/www/kanban/inner.js @@ -930,8 +930,6 @@ define([ //framework._.sfCommon.setPadAttribute('quickMode', false); }); - var toggleTagsButton = h('button.btn.btn-default.kanban-tag-btn-toggle', Messages.kanban_showTags); - // Tags filter var existing = getExistingTags(kanban.options.boards); var list = h('div.cp-kanban-filterTags-list'); @@ -941,7 +939,6 @@ define([ ]); var hint = h('span.cp-kanban-filterTags-name', Messages.kanban_tags); var tags = h('div.cp-kanban-filterTags', [ - h('span.cp-kanban-filterTags-toggle', [ hint, reset, @@ -1022,41 +1019,20 @@ define([ commitTags(); }); + let toggleTagsButton = h('button.btn.btn-default.cp-kanban-toggle-tags', [ + h('i.fa.fa-tags'), + h('span', Messages.fm_tagsName) + ]); - if ($(window).width() < 500) { - - $(tags).append(toggleTagsButton); - - var hideTags = function () { - for (var tag of list.children) { - if (existing.indexOf(tag.innerHTML) > 10) { - $(tag).hide(); - } - } - }; - hideTags(); - - var toggleTags = function () { - for (var tag of list.children) { - if (existing.indexOf(tag.innerHTML) > 10 && kanban.options.tags.indexOf(tag.innerHTML) === -1) { - if ($(tag).is(":visible")) { - $(tag).hide(); - $(toggleTagsButton).text(Messages.kanban_showTags); - } else { - $(tag).show(); - $(toggleTagsButton).text(Messages.kanban_hideTags); - } - } - } - }; - - $(toggleTagsButton).click(function() { - toggleTags(); - }); - - } + let $toggleBtn = $(toggleTagsButton).click(function() { + let $t = $(tags).toggle(); + let visible = $t.is(':visible'); + $toggleBtn.toggleClass('btn-default', visible); + $toggleBtn.toggleClass('btn-default-alt', !visible); + }); var container = h('div#cp-kanban-controls', [ + toggleTagsButton, tags, h('div.cp-kanban-changeView', [ small, @@ -1065,18 +1041,6 @@ define([ ]); $container.before(container); - var common = framework._.sfCommon; - var $button = common.createButton('toggle', true, { - element: $(container), - icon: 'fa-tags', - text: Messages.fm_tagsName, - }, function () { - $button.toggleClass('cp-toolbar-button-active'); - - }); - $button.addClass('cp-toolbar-button-active'); - framework._.toolbar.$bottomL.append($button); - onRedraw.reg(function () { // Redraw if new tags have been added to items var old = Sortify(existing); From 743db4f5705fd6f460bf7a3e7015380138443224 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 7 Mar 2025 16:13:50 +0100 Subject: [PATCH 08/14] Fix realtime cursor issues in kanban --- www/kanban/inner.js | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/www/kanban/inner.js b/www/kanban/inner.js index 947092c59..59356c500 100644 --- a/www/kanban/inner.js +++ b/www/kanban/inner.js @@ -65,6 +65,9 @@ define([ var onCursorUpdate = Util.mkEvent(); var remoteCursors = {}; + let getCursor = () => {}; + let restoreCursor = () => {}; + var setValueAndCursor = function (input, val, _cursor) { if (!input) { return; } var $input = $(input); @@ -153,9 +156,12 @@ define([ var _lastUpdate = 0; var _updateBoards = function (framework, kanban, boards) { _lastUpdate = now(); + var cursor = getCursor(); kanban.setBoards(Util.clone(boards)); kanban.inEditMode = false; addEditItemButton(framework, kanban); + restoreCursor(cursor); + onRemoteChange.fire(); }; var _updateBoardsThrottle = Util.throttle(_updateBoards, 1000); var updateBoards = function (framework, kanban, boards) { @@ -700,8 +706,11 @@ define([ var item = kanban.getItemJSON(eid); item.title = name; kanban.onChange(); - // Unlock edit mode - kanban.inEditMode = false; + // Unlock edit mode unless we're already editing + // something else + if (kanban.inEditMode === eid) { + kanban.inEditMode = false; + } onCursorUpdate.fire({}); }; $input.blur(save); @@ -764,7 +773,9 @@ define([ kanban.getBoardJSON(boardId).title = name; kanban.onChange(); // Unlock edit mode - kanban.inEditMode = false; + if (kanban.inEditMode === boardId) { + kanban.inEditMode = false; + } onCursorUpdate.fire({}); }; $input.blur(save); @@ -819,7 +830,9 @@ define([ }); var save = function () { $item.remove(); - kanban.inEditMode = false; + if (kanban.inEditMode === "new") { + kanban.inEditMode = false; + } onCursorUpdate.fire({}); if (!$input.val()) { return; } var id = Util.createRandomInteger(); @@ -1191,7 +1204,7 @@ define([ $container.find('.kanban-edit-item').remove(); }); - var getCursor = function () { + getCursor = function () { if (!kanban || !kanban.inEditMode) { return; } try { var id = kanban.inEditMode; @@ -1232,7 +1245,7 @@ define([ return {}; } }; - var restoreCursor = function (data) { + restoreCursor = function (data) { if (!data) { return; } try { var id = data.id; @@ -1296,12 +1309,9 @@ define([ var remoteContent = newContent.content; if (Sortify(currentContent) !== Sortify(remoteContent)) { - var cursor = getCursor(); verbose("Content is different.. Applying content"); kanban.options.boards = remoteContent; updateBoards(framework, kanban, remoteContent); - restoreCursor(cursor); - onRemoteChange.fire(); } }); From 1965c939b6e01af8b862b0d9b3c1acb3276dc273 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 7 Mar 2025 16:40:44 +0100 Subject: [PATCH 09/14] Fix initial tags state and UI issue --- .../src/less2/include/colortheme-dark.less | 1 + customize.dist/src/less2/include/forms.less | 4 +-- www/kanban/app-kanban.less | 3 ++ www/kanban/inner.js | 35 ++++++++++++++++--- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/customize.dist/src/less2/include/colortheme-dark.less b/customize.dist/src/less2/include/colortheme-dark.less index 07117b0f9..1e9916267 100644 --- a/customize.dist/src/less2/include/colortheme-dark.less +++ b/customize.dist/src/less2/include/colortheme-dark.less @@ -164,6 +164,7 @@ @cp_buttons-hover: @cryptpad_color_brand_fadest; @cp_buttons-default: @cryptpad_color_grey_700; @cp_buttons-default-color: @cryptpad_text_col; +@cp_buttons-default-alt-color: @cryptpad_color_black; @cp_buttons-default-border: @cryptpad_text_col; @cp_buttons-red: #E55236; @cp_buttons-red-text: @cryptpad_color_light_red; diff --git a/customize.dist/src/less2/include/forms.less b/customize.dist/src/less2/include/forms.less index 0d00204f0..61cc999b2 100644 --- a/customize.dist/src/less2/include/forms.less +++ b/customize.dist/src/less2/include/forms.less @@ -200,8 +200,8 @@ } } &.btn-default-alt { - border-color: @cp_buttons-default; - color: @cp_buttons-default; + border-color: @cp_toolbar-bottom-fg; + color: @cp_toolbar-bottom-fg; background-color: @cp_buttons-default-color; &:hover, &:not(:disabled):active, &:focus { border-color: @cp_buttons-default-color; diff --git a/www/kanban/app-kanban.less b/www/kanban/app-kanban.less index ef0fc7fd8..4e8d645ad 100644 --- a/www/kanban/app-kanban.less +++ b/www/kanban/app-kanban.less @@ -145,6 +145,9 @@ margin-bottom: 15px; } + .cp-kanban-toggle-container.cp-kanban-container-flex { + flex: 1; + } .cp-kanban-toggle-tags { margin-right: 0.5rem; } diff --git a/www/kanban/inner.js b/www/kanban/inner.js index 502da34ad..5050ef749 100644 --- a/www/kanban/inner.js +++ b/www/kanban/inner.js @@ -1023,16 +1023,43 @@ define([ h('i.fa.fa-tags'), h('span', Messages.fm_tagsName) ]); + let toggleContainer = h('div.cp-kanban-toggle-container', toggleTagsButton); - let $toggleBtn = $(toggleTagsButton).click(function() { - let $t = $(tags).toggle(); - let visible = $t.is(':visible'); + let toggleClicked = false; + let $tags = $(tags); + let toggle = () => { + $tags.toggle(); + let visible = $tags.is(':visible'); + $(toggleContainer).toggleClass('cp-kanban-container-flex', !visible); $toggleBtn.toggleClass('btn-default', visible); $toggleBtn.toggleClass('btn-default-alt', !visible); + }; + let $toggleBtn = $(toggleTagsButton).click(function() { + toggleClicked = true; + toggle(); }); + const resizeTags = () => { + if (toggleClicked) { return; } + let visible = $tags.is(':visible'); + // Small screen and visible: hide + if ($(window).width() < 500) { + if (visible) { + $(tags).show(); + toggle(); + } + return; + } + // Large screen: make visible by default + if (visible) { return; } + $(tags).hide(); + toggle(); + }; + + $(window).on('resize', resizeTags); + var container = h('div#cp-kanban-controls', [ - toggleTagsButton, + toggleContainer, tags, h('div.cp-kanban-changeView', [ small, From 10afbca9b0fb5289fa6a4ddc3257b34e09d03eb9 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 7 Mar 2025 16:43:17 +0100 Subject: [PATCH 10/14] Update screen size limit for tags --- www/kanban/inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/kanban/inner.js b/www/kanban/inner.js index 5050ef749..425f859b4 100644 --- a/www/kanban/inner.js +++ b/www/kanban/inner.js @@ -1043,7 +1043,7 @@ define([ if (toggleClicked) { return; } let visible = $tags.is(':visible'); // Small screen and visible: hide - if ($(window).width() < 500) { + if ($(window).width() < 600) { if (visible) { $(tags).show(); toggle(); From a070e3c3449b984e06ef3d468f9902c6de1bbac3 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 7 Mar 2025 17:28:54 +0100 Subject: [PATCH 11/14] Fix tags button style --- www/kanban/app-kanban.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/www/kanban/app-kanban.less b/www/kanban/app-kanban.less index 4e8d645ad..4a81b68b2 100644 --- a/www/kanban/app-kanban.less +++ b/www/kanban/app-kanban.less @@ -149,7 +149,12 @@ flex: 1; } .cp-kanban-toggle-tags { + text-transform: unset; margin-right: 0.5rem; + padding: 3px 10px; + span { + font: @colortheme_app-font; + } } #cp-kanban-edit-tags { .tokenfield { From cbfee93c3a3839ab776aef67b860eb5e0aca9e8a Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 7 Mar 2025 17:53:43 +0100 Subject: [PATCH 12/14] Fix kanban tags issues --- .../src/less2/include/colortheme-dark.less | 2 +- customize.dist/src/less2/include/colortheme.less | 1 + customize.dist/src/less2/include/forms.less | 4 ++-- www/kanban/app-kanban.less | 8 -------- www/kanban/inner.js | 12 ++++++++++-- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/customize.dist/src/less2/include/colortheme-dark.less b/customize.dist/src/less2/include/colortheme-dark.less index 1e9916267..1f5aee211 100644 --- a/customize.dist/src/less2/include/colortheme-dark.less +++ b/customize.dist/src/less2/include/colortheme-dark.less @@ -164,7 +164,7 @@ @cp_buttons-hover: @cryptpad_color_brand_fadest; @cp_buttons-default: @cryptpad_color_grey_700; @cp_buttons-default-color: @cryptpad_text_col; -@cp_buttons-default-alt-color: @cryptpad_color_black; +@cp_buttons-default-alt-color: @cryptpad_color_grey_700; @cp_buttons-default-border: @cryptpad_text_col; @cp_buttons-red: #E55236; @cp_buttons-red-text: @cryptpad_color_light_red; diff --git a/customize.dist/src/less2/include/colortheme.less b/customize.dist/src/less2/include/colortheme.less index faaca5285..ece9f6fe0 100644 --- a/customize.dist/src/less2/include/colortheme.less +++ b/customize.dist/src/less2/include/colortheme.less @@ -164,6 +164,7 @@ @cp_buttons-default: #CCC; @cp_buttons-default-color: @cryptpad_text_col; @cp_buttons-default-border: @cryptpad_text_col; +@cp_buttons-default-alt-color: @cryptpad_color_grey_50; @cp_buttons-red: #E55236; @cp_buttons-red-text: @cp_buttons-red; @cp_buttons-red-color: #FFF; diff --git a/customize.dist/src/less2/include/forms.less b/customize.dist/src/less2/include/forms.less index 61cc999b2..745d24ab0 100644 --- a/customize.dist/src/less2/include/forms.less +++ b/customize.dist/src/less2/include/forms.less @@ -200,8 +200,8 @@ } } &.btn-default-alt { - border-color: @cp_toolbar-bottom-fg; - color: @cp_toolbar-bottom-fg; + border-color: @cp_buttons-default-alt-color; + color: @cp_buttons-default-alt-color; background-color: @cp_buttons-default-color; &:hover, &:not(:disabled):active, &:focus { border-color: @cp_buttons-default-color; diff --git a/www/kanban/app-kanban.less b/www/kanban/app-kanban.less index 4a81b68b2..b050791b7 100644 --- a/www/kanban/app-kanban.less +++ b/www/kanban/app-kanban.less @@ -454,16 +454,8 @@ } flex-flow: column; flex-shrink: 0; - & > * { - visibility: hidden; - } & > span { display: inline-block; - height: 38px; - line-height: 38px; - } - & > button { - margin-top: -38px; } } button.cp-kanban-filterTags-reset { diff --git a/www/kanban/inner.js b/www/kanban/inner.js index 425f859b4..df9a676f1 100644 --- a/www/kanban/inner.js +++ b/www/kanban/inner.js @@ -933,9 +933,9 @@ define([ // Tags filter var existing = getExistingTags(kanban.options.boards); var list = h('div.cp-kanban-filterTags-list'); - var reset = h('button.btn.btn-cancel.cp-kanban-filterTags-reset', [ + var reset = h('button.btn.btn-cancel.cp-kanban-filterTags-reset.cp-kanban-toggle-tags', [ h('i.fa.fa-times'), - Messages.kanban_clearFilter + h('span', Messages.kanban_clearFilter) ]); var hint = h('span.cp-kanban-filterTags-name', Messages.kanban_tags); var tags = h('div.cp-kanban-filterTags', [ @@ -951,8 +951,16 @@ define([ var $hint = $(hint); var setTagFilterState = function (bool) { + //$hint.toggle(!bool); + //$reset.toggle(!!bool); $hint.css('visibility', bool? 'hidden': 'visible'); + $hint.css('height', bool ? 0 : ''); + $hint.css('padding-top', bool ? 0 : ''); + $hint.css('padding-bottom', bool ? 0 : ''); $reset.css('visibility', bool? 'visible': 'hidden'); + $reset.css('height', !bool ? 0 : ''); + $reset.css('padding-top', !bool ? 0 : ''); + $reset.css('padding-bottom', !bool ? 0 : ''); }; setTagFilterState(); From d1799d106cd45b1f97251f9e0451cd519eb169d0 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 12 Mar 2025 16:34:24 +0100 Subject: [PATCH 13/14] New OnlyOffice default config options --- www/common/onlyoffice/inner.js | 10 +++++++++- www/common/sframe-boot2.js | 11 +++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 07c2ad1e3..614c7e7ce 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -1692,11 +1692,14 @@ define([ "documentType": file.doc, "editorConfig": { customization: { + compactHeader: true, chat: false, logo: { url: "/bounce/#" + encodeURIComponent('https://www.onlyoffice.com') }, - comments: !lock && !readOnly + comments: !lock && !readOnly, + hideRightMenu: true, + uiTheme: window.CryptPad_theme === "dark" ? "theme-dark" : "theme-classic-light" }, "user": { "id": String(myOOId), //"c0c3bf82-20d7-4663-bf6d-7fa39c598b1d", @@ -2132,6 +2135,11 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null }, void 0, common.getCache()); }; + // Always hide right menu + localStorage?.original.removeItem('sse-hide-right-settings'); + localStorage?.original.removeItem('de-hide-right-settings'); + localStorage?.original.removeItem('pe-hide-right-settings'); + APP.docEditor = new window.DocsAPI.DocEditor("cp-app-oo-placeholder-a", APP.ooconfig); ooLoaded = true; makeChannel(); diff --git a/www/common/sframe-boot2.js b/www/common/sframe-boot2.js index 4df642678..359424a5d 100644 --- a/www/common/sframe-boot2.js +++ b/www/common/sframe-boot2.js @@ -25,16 +25,19 @@ define([ delete ls[k]; }); }; - var mkFakeStore = function () { + var mkFakeStore = function (original) { var fakeStorage = { getItem: function (k) { return fakeStorage[k]; }, setItem: function (k, v) { fakeStorage[k] = v; return v; }, - removeItem: function (k) { delete fakeStorage[k]; } + removeItem: function (k) { delete fakeStorage[k]; }, + original }; return fakeStorage; }; - window.__defineGetter__('localStorage', function () { return mkFakeStore(); }); - window.__defineGetter__('sessionStorage', function () { return mkFakeStore(); }); + let loc = localStorage; + let ses = sessionStorage; + window.__defineGetter__('localStorage', function () { return mkFakeStore(loc); }); + window.__defineGetter__('sessionStorage', function () { return mkFakeStore(ses); }); window.CRYPTPAD_INSIDE = true; From 62437e0f5b2a81360a1e6abaab0db4569699f207 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 12 Mar 2025 18:00:07 +0100 Subject: [PATCH 14/14] lint compliance --- www/kanban/inner.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/www/kanban/inner.js b/www/kanban/inner.js index ffe9405fe..fa7103aa5 100644 --- a/www/kanban/inner.js +++ b/www/kanban/inner.js @@ -152,6 +152,7 @@ define([ var addEditItemButton = function () {}; + var onRemoteChange = Util.mkEvent(); var now = function () { return +new Date(); }; var _lastUpdate = 0; var _updateBoards = function (framework, kanban, boards) { @@ -172,7 +173,6 @@ define([ _updateBoardsThrottle(framework, kanban, boards); }; - var onRemoteChange = Util.mkEvent(); var editModal; var PROPERTIES = ['title', 'body', 'tags', 'color']; var BOARD_PROPERTIES = ['title', 'color']; @@ -1048,6 +1048,7 @@ define([ let toggleClicked = false; let $tags = $(tags); + let $toggleBtn = $(toggleTagsButton); let toggle = () => { $tags.toggle(); let visible = $tags.is(':visible'); @@ -1055,7 +1056,7 @@ define([ $toggleBtn.toggleClass('btn-default', visible); $toggleBtn.toggleClass('btn-default-alt', !visible); }; - let $toggleBtn = $(toggleTagsButton).click(function() { + $toggleBtn.click(function() { toggleClicked = true; toggle(); });