From 3aa88e61ef5966fc8d93cc2e844093c38a93a1da Mon Sep 17 00:00:00 2001 From: Cthomp7 Date: Tue, 16 Sep 2025 06:34:47 -0500 Subject: [PATCH 01/25] split keyframe & tutorial tools in nav --- www/widgets/tutorial-maker/popups/index.html | 16 +++++++++++----- www/widgets/tutorial-maker/popups/main.css | 7 +++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/www/widgets/tutorial-maker/popups/index.html b/www/widgets/tutorial-maker/popups/index.html index 1d9a3a4e..f35ad677 100644 --- a/www/widgets/tutorial-maker/popups/index.html +++ b/www/widgets/tutorial-maker/popups/index.html @@ -123,11 +123,17 @@

Video Recorder

diff --git a/www/widgets/tutorial-maker/popups/main.css b/www/widgets/tutorial-maker/popups/main.css index 6d2de16d..be322ea8 100644 --- a/www/widgets/tutorial-maker/popups/main.css +++ b/www/widgets/tutorial-maker/popups/main.css @@ -55,6 +55,13 @@ nav { padding: 10px; } +nav > div { + display: flex; + justify-content: space-between; + align-items: center; + gap: 7px; +} + /* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Overlay Panels ~ ~ ~ ~ ~ ~ ~ ~ */ .overlay { From 776e92c799206f71cb06095dab4da0ebffc8236f Mon Sep 17 00:00:00 2001 From: Cthomp7 Date: Thu, 18 Sep 2025 23:37:20 -0500 Subject: [PATCH 02/25] added keyframe name logic --- www/widgets/tutorial-maker/index.js | 2 +- www/widgets/tutorial-maker/popups/index.html | 1 + www/widgets/tutorial-maker/popups/main.css | 37 ++++++++++++++++++++ www/widgets/tutorial-maker/popups/main.js | 26 +++++++++----- 4 files changed, 56 insertions(+), 10 deletions(-) diff --git a/www/widgets/tutorial-maker/index.js b/www/widgets/tutorial-maker/index.js index 26805716..fc4510db 100644 --- a/www/widgets/tutorial-maker/index.js +++ b/www/widgets/tutorial-maker/index.js @@ -132,7 +132,7 @@ class TutorialMaker extends Widget { if (!frame) { // create keyframe frame = { timecode, - name: `keyframe ${(this.hvp.data[type].length + 1).toString()}`, + name: `keyframe: ${timecode}`, video: this._getSizeAndPosition(this.hvp), widgets: this._getCurrentWidgets(), netitor: this._getNetitorData(), diff --git a/www/widgets/tutorial-maker/popups/index.html b/www/widgets/tutorial-maker/popups/index.html index f35ad677..636f0c15 100644 --- a/www/widgets/tutorial-maker/popups/index.html +++ b/www/widgets/tutorial-maker/popups/index.html @@ -126,6 +126,7 @@

Video Recorder

+
diff --git a/www/widgets/tutorial-maker/popups/main.css b/www/widgets/tutorial-maker/popups/main.css index be322ea8..72f2f83b 100644 --- a/www/widgets/tutorial-maker/popups/main.css +++ b/www/widgets/tutorial-maker/popups/main.css @@ -62,6 +62,43 @@ nav > div { gap: 7px; } +nav > div > div { + display: flex; + align-items: center; + gap: 7px; +} + +.keyframe-tools { + flex-grow: 1; +} +.tutorial-tools { + flex: 0; +} + +#kf-name-input { + border: 1px solid var(--netizen-meta); + background: var(--bg-color); + color: var(--netizen-meta); + padding: 4px 10px; + font-weight: 600; + border-radius: 7px; + max-width: 320px; + width: 100%; + white-space: nowrap; + overflow-x: auto; + overflow-y: hidden; + scrollbar-width: none; + -ms-overflow-style: none; +} +#kf-name-input::-webkit-scrollbar { + display: none; +} +#kf-name-input:empty:before { + content: attr(data-placeholder); + color: var(--netizen-hint-shadow); + pointer-events: none; + } + /* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Overlay Panels ~ ~ ~ ~ ~ ~ ~ ~ */ .overlay { diff --git a/www/widgets/tutorial-maker/popups/main.js b/www/widgets/tutorial-maker/popups/main.js index cd9d36c3..d01c9a2b 100644 --- a/www/widgets/tutorial-maker/popups/main.js +++ b/www/widgets/tutorial-maker/popups/main.js @@ -112,18 +112,25 @@ function addMarker (k, type) { const x = t / metadata.duration * 100 timeline.createMarker(x, t, type, (tc) => { msg(`tut-mkr-${type}-click`, tc) + if (type === 'keyframe') setNameInput(tc) }) + if (type === 'keyframe') nn.get(`[name="keyframe-${k.timecode}"]`).dataset.name = k.name } -function updateMarker (k, type) { - // TODO needs configured +function createKeyframe (kf) { + addMarker(kf, 'keyframe') + timeline.updateMarkers() } -function updateKeyframe (data) { - const { frame, add } = data - if (add) addMarker(frame, 'keyframe') - else updateMarker(frame, 'keyframe') - timeline.updateMarkers() +function updateKeframe () { + const name = nn.get('#kf-name-input').textContent + msg('tut-mkr-get-keyframe', { name, timecode: TIMECODE }) + nn.get(`[name="keyframe-${TIMECODE}"]`).dataset.name = name +} + +function setNameInput (tc) { + const name = nn.get(`[name="keyframe-${tc}"]`).dataset.name + nn.get('#kf-name-input').textContent = name } // -------------------------------------------------------- SETUP EVENT LISTENRS @@ -132,7 +139,7 @@ nn.get('#new').on('click', () => overlay('#metadata')) nn.get('#open').on('click', openTutorial) // nn.get('#close-recorder').on('click', updateVideo) nn.get('#open-metadata').on('click', () => msg('tut-mkr-get-metadata')) -nn.get('#update-keyframe').on('click', () => msg('tut-mkr-get-keyframe', { timecode: TIMECODE })) +nn.get('#update-keyframe').on('click', () => updateKeframe()) nn.get('#download-tutorial').on('click', () => zipper.download()) nn.getAll('button[name]').forEach(btn => { @@ -141,6 +148,7 @@ nn.getAll('button[name]').forEach(btn => { }) }) nn.get('#create-keyframe').on('click', () => msg('tut-mkr-get-keyframe', { timecode: TIMECODE })) +nn.get('#kf-name-input').on('keydown', (e) => { e.stopPropagation() }) // prevents shortcuts when typing // .................... window events @@ -184,7 +192,7 @@ nn.on('message', (e) => { } else if (type === 'tut-mkr-video-duration') { metadata.duration = payload } else if (type === 'tut-mkr-keyframe') { - updateKeyframe(payload) + if (payload.add) createKeyframe(payload.frame) } else if (type === 'tut-mkr-keylog') { // TODO... } else if (type === 'tut-mkr-time-update') { From ef5e504c850cd04df6b2d6b55ee7ab67ba830a6c Mon Sep 17 00:00:00 2001 From: Cthomp7 Date: Thu, 18 Sep 2025 23:42:41 -0500 Subject: [PATCH 03/25] removed preset keyframe name --- www/widgets/tutorial-maker/index.js | 2 +- www/widgets/tutorial-maker/popups/main.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/www/widgets/tutorial-maker/index.js b/www/widgets/tutorial-maker/index.js index fc4510db..4b4c35e3 100644 --- a/www/widgets/tutorial-maker/index.js +++ b/www/widgets/tutorial-maker/index.js @@ -132,7 +132,7 @@ class TutorialMaker extends Widget { if (!frame) { // create keyframe frame = { timecode, - name: `keyframe: ${timecode}`, + name: name ?? '', video: this._getSizeAndPosition(this.hvp), widgets: this._getCurrentWidgets(), netitor: this._getNetitorData(), diff --git a/www/widgets/tutorial-maker/popups/main.js b/www/widgets/tutorial-maker/popups/main.js index d01c9a2b..03cf88bf 100644 --- a/www/widgets/tutorial-maker/popups/main.js +++ b/www/widgets/tutorial-maker/popups/main.js @@ -123,7 +123,7 @@ function createKeyframe (kf) { } function updateKeframe () { - const name = nn.get('#kf-name-input').textContent + const name = document.querySelector('#kf-name-input')?.textContent msg('tut-mkr-get-keyframe', { name, timecode: TIMECODE }) nn.get(`[name="keyframe-${TIMECODE}"]`).dataset.name = name } From f87324a5fe0897c8d4f0294ce8f34610e3956b06 Mon Sep 17 00:00:00 2001 From: Cthomp7 Date: Fri, 19 Sep 2025 00:05:20 -0500 Subject: [PATCH 04/25] only show keyframe editing tools when keyframe is selected --- www/widgets/tutorial-maker/popups/index.html | 6 +++--- www/widgets/tutorial-maker/popups/main.css | 2 +- www/widgets/tutorial-maker/popups/main.js | 11 +++++++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/www/widgets/tutorial-maker/popups/index.html b/www/widgets/tutorial-maker/popups/index.html index 636f0c15..9294fd07 100644 --- a/www/widgets/tutorial-maker/popups/index.html +++ b/www/widgets/tutorial-maker/popups/index.html @@ -126,9 +126,9 @@

Video Recorder

-
- - +
+ +
diff --git a/www/widgets/tutorial-maker/popups/main.css b/www/widgets/tutorial-maker/popups/main.css index 72f2f83b..46815228 100644 --- a/www/widgets/tutorial-maker/popups/main.css +++ b/www/widgets/tutorial-maker/popups/main.css @@ -79,7 +79,7 @@ nav > div > div { border: 1px solid var(--netizen-meta); background: var(--bg-color); color: var(--netizen-meta); - padding: 4px 10px; + padding: 2.5px 10px; font-weight: 600; border-radius: 7px; max-width: 320px; diff --git a/www/widgets/tutorial-maker/popups/main.js b/www/widgets/tutorial-maker/popups/main.js index 03cf88bf..fd5f64cc 100644 --- a/www/widgets/tutorial-maker/popups/main.js +++ b/www/widgets/tutorial-maker/popups/main.js @@ -89,6 +89,9 @@ function videoTimeUpdated (obj) { // runs as video plays && it's time udpates if (obj.keyframe) { const marker = nn.get(`[name="keyframe-${obj.keyframe.timecode}"]`) timeline.selectMarker(marker) + keyframeEditMode(true) + } else { + keyframeEditMode(false) } if (obj.keylog) { const marker = nn.get(`[name="keylog-${obj.keylog.timecode}"]`) @@ -128,6 +131,12 @@ function updateKeframe () { nn.get(`[name="keyframe-${TIMECODE}"]`).dataset.name = name } +function keyframeEditMode (enable) { + const tools = nn.getAll('[kf-edit-tool]') + if (enable) tools.forEach((t) => { t.style.display = 'block' }) + else tools.forEach((t) => { t.style.display = 'none' }) +} + function setNameInput (tc) { const name = nn.get(`[name="keyframe-${tc}"]`).dataset.name nn.get('#kf-name-input').textContent = name @@ -159,6 +168,8 @@ nn.on('load', () => { } timeline.init() + keyframeEditMode(false) + const m = document.createElement('tutorial-modal') nn.get('body').appendChild(m) modal = m From 6b83ee2bb75e7132baf640c591cc40791f6e145c Mon Sep 17 00:00:00 2001 From: Cthomp7 Date: Mon, 22 Sep 2025 21:34:48 -0500 Subject: [PATCH 05/25] added deleting keyframe logic --- www/widgets/tutorial-maker/index.js | 11 ++++++++++- www/widgets/tutorial-maker/popups/index.html | 3 ++- www/widgets/tutorial-maker/popups/main.js | 12 +++++++++--- www/widgets/tutorial-maker/popups/timeline.js | 11 +++++++++++ 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/www/widgets/tutorial-maker/index.js b/www/widgets/tutorial-maker/index.js index 4b4c35e3..ed26bf5e 100644 --- a/www/widgets/tutorial-maker/index.js +++ b/www/widgets/tutorial-maker/index.js @@ -129,7 +129,16 @@ class TutorialMaker extends Widget { const { timecode, name } = data let frame = this.hvp.data[type].find(k => k.timecode === timecode) if (type === 'keyframes') { - if (!frame) { // create keyframe + if (data?.remove) { // delete keyframe + if (frame) { + this.hvp.data[type].splice(this.hvp.data[type].indexOf(frame), 1) + return { frame, remove: true } + } else { + const error = `Failed to remove keyframe. No keyframe found for: ${timecode} seconds` + console.error(error) + return { error } + } + } else if (!frame) { // create keyframe frame = { timecode, name: name ?? '', diff --git a/www/widgets/tutorial-maker/popups/index.html b/www/widgets/tutorial-maker/popups/index.html index 9294fd07..d057bd08 100644 --- a/www/widgets/tutorial-maker/popups/index.html +++ b/www/widgets/tutorial-maker/popups/index.html @@ -128,7 +128,8 @@

Video Recorder

- + +
diff --git a/www/widgets/tutorial-maker/popups/main.js b/www/widgets/tutorial-maker/popups/main.js index fd5f64cc..f76df0d5 100644 --- a/www/widgets/tutorial-maker/popups/main.js +++ b/www/widgets/tutorial-maker/popups/main.js @@ -125,12 +125,17 @@ function createKeyframe (kf) { timeline.updateMarkers() } -function updateKeframe () { +function updateKeyframe () { const name = document.querySelector('#kf-name-input')?.textContent msg('tut-mkr-get-keyframe', { name, timecode: TIMECODE }) nn.get(`[name="keyframe-${TIMECODE}"]`).dataset.name = name } +function deleteKeyframe () { + timeline.removeMarker(TIMECODE, 'keyframe') + msg('tut-mkr-get-keyframe', { timecode: TIMECODE, remove: true }) +} + function keyframeEditMode (enable) { const tools = nn.getAll('[kf-edit-tool]') if (enable) tools.forEach((t) => { t.style.display = 'block' }) @@ -148,7 +153,9 @@ nn.get('#new').on('click', () => overlay('#metadata')) nn.get('#open').on('click', openTutorial) // nn.get('#close-recorder').on('click', updateVideo) nn.get('#open-metadata').on('click', () => msg('tut-mkr-get-metadata')) -nn.get('#update-keyframe').on('click', () => updateKeframe()) +nn.get('#create-keyframe').on('click', () => msg('tut-mkr-get-keyframe', { timecode: TIMECODE })) +nn.get('#update-keyframe').on('click', () => updateKeyframe()) +nn.get('#delete-keyframe').on('click', () => deleteKeyframe()) nn.get('#download-tutorial').on('click', () => zipper.download()) nn.getAll('button[name]').forEach(btn => { @@ -156,7 +163,6 @@ nn.getAll('button[name]').forEach(btn => { msg('tut-mkr-explain', btn.getAttribute('name')) }) }) -nn.get('#create-keyframe').on('click', () => msg('tut-mkr-get-keyframe', { timecode: TIMECODE })) nn.get('#kf-name-input').on('keydown', (e) => { e.stopPropagation() }) // prevents shortcuts when typing // .................... window events diff --git a/www/widgets/tutorial-maker/popups/timeline.js b/www/widgets/tutorial-maker/popups/timeline.js index 62f148bd..950a9fd5 100644 --- a/www/widgets/tutorial-maker/popups/timeline.js +++ b/www/widgets/tutorial-maker/popups/timeline.js @@ -165,6 +165,17 @@ const timeline = { if (Number(list[i].timecode) < t) return list[i] } return null + }, + + removeMarker: (time, type) => { + const t = Number(time) + const m = nn.get(`[name="${type}-${t}"]`) + if (m) { + m.remove() + timeline.updateMarkers() + } else { + console.error(`Failed to remove marker. No Marker found for: ${time} seconds`) + } } } From a046d601399aa452f84c8eeeaad1e99b462ca54a Mon Sep 17 00:00:00 2001 From: Cthomp7 Date: Mon, 22 Sep 2025 21:51:09 -0500 Subject: [PATCH 06/25] configured keyframes and keylogs in zipper.download --- www/widgets/tutorial-maker/index.js | 10 ++++++++++ www/widgets/tutorial-maker/popups/main.js | 4 +++- www/widgets/tutorial-maker/popups/recorder.js | 2 +- www/widgets/tutorial-maker/popups/zip-tools.js | 10 +++++----- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/www/widgets/tutorial-maker/index.js b/www/widgets/tutorial-maker/index.js index ed26bf5e..dc5d67d7 100644 --- a/www/widgets/tutorial-maker/index.js +++ b/www/widgets/tutorial-maker/index.js @@ -28,6 +28,9 @@ class TutorialMaker extends Widget { if (e.origin !== window.location.origin) return // for security if (type === 'tut-mkr-opened-tutorial') { // opened tutorial from zip this._loadTutorial(payload) + } else if (type === 'tut-mkr-get-data') { // get tutorial data + const data = this._getData() + this._messagePopup('tut-mkr-data', data) } else if (type === 'tut-mkr-update-metadata') { // new metadata this._updateMetadata(payload) } else if (type === 'tut-mkr-get-metadata') { // asked for metadata @@ -163,6 +166,13 @@ class TutorialMaker extends Widget { return { frame } } + _getData () { + const keyframes = this.hvp.data.keyframes + const keylogs = this.hvp.data.keylogs + // TODO: fetch widgets + return { keyframes, keylogs } + } + // ............................ helpers ...................................... _openPopup (type, payload) { diff --git a/www/widgets/tutorial-maker/popups/main.js b/www/widgets/tutorial-maker/popups/main.js index f76df0d5..d8b27546 100644 --- a/www/widgets/tutorial-maker/popups/main.js +++ b/www/widgets/tutorial-maker/popups/main.js @@ -156,7 +156,7 @@ nn.get('#open-metadata').on('click', () => msg('tut-mkr-get-metadata')) nn.get('#create-keyframe').on('click', () => msg('tut-mkr-get-keyframe', { timecode: TIMECODE })) nn.get('#update-keyframe').on('click', () => updateKeyframe()) nn.get('#delete-keyframe').on('click', () => deleteKeyframe()) -nn.get('#download-tutorial').on('click', () => zipper.download()) +nn.get('#download-tutorial').on('click', () => msg('tut-mkr-get-data')) nn.getAll('button[name]').forEach(btn => { btn.on('click', () => { @@ -214,6 +214,8 @@ nn.on('message', (e) => { // TODO... } else if (type === 'tut-mkr-time-update') { videoTimeUpdated(payload) + } else if (type === 'tut-mkr-data') { + zipper.download(payload) } }) diff --git a/www/widgets/tutorial-maker/popups/recorder.js b/www/widgets/tutorial-maker/popups/recorder.js index 98f3a67c..cacd7391 100644 --- a/www/widgets/tutorial-maker/popups/recorder.js +++ b/www/widgets/tutorial-maker/popups/recorder.js @@ -243,7 +243,7 @@ const recorder = { ` modal.openWithHTML(innerHTML) nn.get('[name="vfn-upload"]').addEventListener('click', () => recorder.onUploadVFN()) - nn.get('[name="vfn-download"]').addEventListener('click', () => zipper.download(recorder.blob)) + nn.get('[name="vfn-download"]').addEventListener('click', () => zipper.download({ video: recorder.blob })) }, onUploadVFN: () => { diff --git a/www/widgets/tutorial-maker/popups/zip-tools.js b/www/widgets/tutorial-maker/popups/zip-tools.js index 73d4ebd8..b2505328 100644 --- a/www/widgets/tutorial-maker/popups/zip-tools.js +++ b/www/widgets/tutorial-maker/popups/zip-tools.js @@ -86,16 +86,16 @@ const zipper = { download: async (data = null) => { try { - const video = data || FILES.readFile(`${metadata.id}.mp4`) - const tutorial = { metadata: {}, widgets: {}, keyframes: {}, keylogs: {} } + const video = data?.video || FILES.readFile(`${metadata.id}.mp4`) + const tutorial = { metadata: {}, widgets: {}, keyframes: [], keylogs: [] } // set metadata const keys = ['id', 'title', 'author', 'authorURL', 'duration', 'jsfile', 'description', 'keywords', 'thumbnails'] keys.forEach(key => { tutorial.metadata[key] = metadata[key] }) // TODO: configure widgets - // TODO: configure keyframes - // TODO: configure keylogs + tutorial.keyframes = data?.keyframes ?? [] + tutorial.keylogs = data?.keylogs ?? [] const json = JSON.stringify(tutorial, null, 2) @@ -108,7 +108,7 @@ const zipper = { const zip = new JSZip() zip.file('tutorial.json', json) zip.file( - data + data?.video ? metadata.id + (recorder.mimeType.includes('mp4') ? '.mp4' : '.webm') : `${metadata.id}.mp4`, blob, From bd9f04f7cbe1d68d884a2f4df556f1f67a3826ba Mon Sep 17 00:00:00 2001 From: Cthomp7 Date: Mon, 22 Sep 2025 22:38:00 -0500 Subject: [PATCH 07/25] in-progress add menu --- www/widgets/tutorial-maker/popups/index.html | 23 ++++++++++++- www/widgets/tutorial-maker/popups/main.css | 33 ++++++++++++++++++- www/widgets/tutorial-maker/popups/main.js | 19 +++++++++++ .../tutorial-maker/popups/recorder.css | 9 +++-- 4 files changed, 79 insertions(+), 5 deletions(-) diff --git a/www/widgets/tutorial-maker/popups/index.html b/www/widgets/tutorial-maker/popups/index.html index d057bd08..ba173a04 100644 --- a/www/widgets/tutorial-maker/popups/index.html +++ b/www/widgets/tutorial-maker/popups/index.html @@ -125,7 +125,28 @@

Video Recorder