From 32c950de5b031c3ba6e32b41698209776aac902e Mon Sep 17 00:00:00 2001 From: Nariman Jelveh Date: Tue, 11 Aug 2026 16:47:12 -0700 Subject: [PATCH] feat: enable app feedback by default in Dev Center New apps created in Dev Center now have feedbackEnabled set on creation, so users can send the developer feedback without any extra setup. A "User Feedback" toggle in the app's settings lets developers turn it off (and back on); it's wired into the save payload, the dirty-state tracking, and the reset-to-original path like the neighboring toggles. The Save update omits feedbackEnabled unless the toggle is present, and the backend leaves an omitted field untouched, so the default survives the create-then-save flow Dev Center runs. Add an SDK apps-suite guard covering that round-trip (create-on -> unrelated update keeps it -> can be turned off). --- src/dev-center/js/apps.js | 15 +++++++++++++- src/puter-js/tests/api/suites/apps.suite.ts | 22 +++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/dev-center/js/apps.js b/src/dev-center/js/apps.js index 2be245f5b..39dae8cb6 100644 --- a/src/dev-center/js/apps.js +++ b/src/dev-center/js/apps.js @@ -191,6 +191,9 @@ async function create_app (title, source_path = null, items = null) { maximizeOnStart: false, background: false, dedupeName: true, + // New apps accept user feedback by default; developers can turn this + // off in the app's settings (the "User Feedback" toggle). + feedbackEnabled: true, metadata: { window_resizable: true, fullpage_on_landing: true, @@ -509,6 +512,12 @@ function generate_edit_app_section (app) {

Misc

+
+ + +

When enabled, users can send you feedback about this app from within Puter. Feedback is delivered to your account's email address.

+
+
@@ -586,6 +595,7 @@ function trackOriginalValues () { locked: $('#edit-app-locked').is(':checked'), fullPageOnLanding: $('#edit-app-fullpage-on-landing').is(':checked'), setTitleToFile: $('#edit-app-set-title-to-file').is(':checked'), + userFeedback: $('#edit-app-user-feedback').is(':checked'), }, }; } @@ -626,7 +636,8 @@ function hasChanges () { $('#edit-app-hide-titlebar').is(':checked') !== originalValues.checkboxes.hideTitleBar || $('#edit-app-locked').is(':checked') !== originalValues.checkboxes.locked || $('#edit-app-fullpage-on-landing').is(':checked') !== originalValues.checkboxes.fullPageOnLanding || - $('#edit-app-set-title-to-file').is(':checked') !== originalValues.checkboxes.setTitleToFile + $('#edit-app-set-title-to-file').is(':checked') !== originalValues.checkboxes.setTitleToFile || + $('#edit-app-user-feedback').is(':checked') !== originalValues.checkboxes.userFeedback ); } @@ -674,6 +685,7 @@ function resetToOriginalValues () { $('#edit-app-locked').prop('checked', originalValues.checkboxes.locked); $('#edit-app-fullpage-on-landing').prop('checked', originalValues.checkboxes.fullPageOnLanding); $('#edit-app-set-title-to-file').prop('checked', originalValues.checkboxes.setTitleToFile); + $('#edit-app-user-feedback').prop('checked', originalValues.checkboxes.userFeedback); if ( originalValues.icon ) { $('#edit-app-icon').css('background-image', `url(${originalValues.icon})`); @@ -1284,6 +1296,7 @@ async function saveEditedApp (mode) { description: description, maximizeOnStart: $('#edit-app-maximize-on-start').is(':checked'), background: $('#edit-app-background').is(':checked'), + feedbackEnabled: $('#edit-app-user-feedback').is(':checked'), metadata: { fullpage_on_landing: $('#edit-app-fullpage-on-landing').is(':checked'), social_image: socialImageUrl, diff --git a/src/puter-js/tests/api/suites/apps.suite.ts b/src/puter-js/tests/api/suites/apps.suite.ts index 12aa6a805..8ee2e0e26 100644 --- a/src/puter-js/tests/api/suites/apps.suite.ts +++ b/src/puter-js/tests/api/suites/apps.suite.ts @@ -144,6 +144,28 @@ export default suite('apps', { t.assert.equal(updated.description, 'New description'); }, + 'feedbackEnabled round-trips on create and survives an unrelated update': + async (t) => { + // Mirrors Dev Center: create with feedback on, then a Save-style + // update that omits feedbackEnabled must not clear it. + const created = await t.puter.apps.create({ + name: 'apps-suite-feedback', + indexURL: 'https://example.com/feedback', + feedbackEnabled: true, + }); + t.assert.equal(Boolean(created.feedback_enabled), true); + + const updated = await t.puter.apps.update('apps-suite-feedback', { + title: 'Feedback App', + }); + t.assert.equal(Boolean(updated.feedback_enabled), true); + + const disabled = await t.puter.apps.update('apps-suite-feedback', { + feedbackEnabled: false, + }); + t.assert.equal(Boolean(disabled.feedback_enabled), false); + }, + 'get of an unknown app rejects': async (t) => { await t.assert.rejects( () => t.puter.apps.get('apps-suite-does-not-exist'),