Skip to content

Commit 05766f7

Browse files
committed
feat: space-aware share flow with pre-share picker and unified urls
1 parent ef24880 commit 05766f7

6 files changed

Lines changed: 78 additions & 78 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ TextAgent has undergone significant evolution since its inception. What started
538538

539539
| Date | Commits | Feature / Update |
540540
|------|---------|-----------------:|
541+
| **2026-03-28** | | 🔗 **Space-Aware Sharing** — moved "Add to Space" dropdown to the pre-share modal; generates unified `#space=<slug>&s=<id>` URLs that load the document within its space context; converted management items to clickable anchor tags opening directly to the doc |
541542
| **2026-03-28** | | 🎓 **Quiz Progress Bar Fix** — fixed progress bar not syncing with respondent navigation; bar now tracks current question position instead of answered-question count; `gotoScreen()` now updates HUD on every navigation |
542543
| **2026-03-27** | | 📊 **ECharts Chart System** — new `{{Chart:}}` DocGen tag with 7 declarative chart types (bar, line, pie, scatter, radar, gauge, heatmap) and raw ECharts JS code mode; `chart-docgen.js` (~720 lines) parser/builder/transformer; `chart-docgen.css` + `echarts.css` styling; lazy-loaded ECharts CDN via `window.getECharts()`; 📊 Chart toolbar button, composer chip, mobile integration; 11 chart gallery templates (Line, Bar, Pie, Scatter, Sunburst, Treemap, Advanced, Sankey, Parallel, Graph) with ~4,200 lines of copy-paste-ready examples; new Charts template category |
543544
| **2026-03-27** | | 📂 **Spaces** — personal document hub with email-based ownership and access key recovery; `space-manager.js` (~760 lines) CRUD, Firestore sync, hub rendering; `spaces.css` (~540 lines) glassmorphic modal UI; Spaces modal with create/recover/manage views; "Add to Space" picker in share modal; `#space=<slug>` URL routing; Firestore `/spaces/{spaceId}` collection rules with field validation, write-token ownership, 50-item limit |
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Space-Aware Share Flow Updates
2+
3+
**Date:** March 28, 2026
4+
5+
## Overview
6+
Re-engineered the "Add to Space" feature to be part of the pre-share workflow, ensuring users can generate a single, meaningful URL that includes both the space context and the document itself.
7+
8+
## Changes
9+
- **Pre-Share Space Selection:** Moved the "Add to Space" dropdown from the post-share result modal into the initial Share Options modal.
10+
- **Unified URL Generation:** Sharing a document to a space now simultaneously creates the share link and associates it with the space, returning a unified URL (`#space=<slug>&s=<id>`).
11+
- **Enhanced Document Routing:** Updated the hash parser (`M.loadSharedMarkdown`) to properly read and execute compound URLs containing both `space` and `s` parameters, ensuring the document loads seamlessly within the space context.
12+
- **Clickable Space Management Items:** Converted the document titles in the "My Spaces" editor modal into clickable anchor tags (`<a>`) that open the shared document in a new tab using the new context-aware URL format.
13+
- **UI Polish:** Added focus states, hover effects, and cleaner typography to the new select dropdown and management links in `spaces.css`.
14+
15+
## Impact
16+
- **No More Duplicate Links:** Eliminates user confusion by generating a firm link only *after* all contexts (like spaces) have been selected.
17+
- **Better UX:** Managers can now easily review the documents they've added to a space directly from their management modal with a single click.

css/spaces.css

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -288,11 +288,18 @@
288288
min-width: 0;
289289
}
290290
.spaces-editor-item-title {
291+
display: block;
291292
font-size: 14px;
292293
color: var(--text-color);
293294
white-space: nowrap;
294295
overflow: hidden;
295296
text-overflow: ellipsis;
297+
text-decoration: none;
298+
transition: color 0.15s;
299+
}
300+
a.spaces-editor-item-title:hover {
301+
color: var(--accent-color);
302+
text-decoration: underline;
296303
}
297304
.spaces-editor-item-time {
298305
font-size: 11px;
@@ -335,47 +342,22 @@
335342
box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.15);
336343
}
337344

338-
/* --- "Add to Space" in Share Result --- */
339-
.share-add-space-section {
340-
margin-top: 12px;
341-
padding-top: 12px;
342-
border-top: 1px solid var(--border-color);
343-
display: flex;
344-
align-items: center;
345-
gap: 8px;
346-
flex-wrap: wrap;
347-
}
348-
.share-add-space-section label {
349-
font-size: 13px;
350-
font-weight: 600;
351-
color: var(--text-color);
352-
white-space: nowrap;
353-
}
354-
.share-add-space-section select {
355-
flex: 1;
356-
min-width: 120px;
357-
padding: 5px 10px;
358-
font-size: 13px;
345+
/* --- "Add to Space" in Share Options --- */
346+
.share-space-select {
347+
width: 100%;
348+
padding: 8px 12px;
349+
font-size: 14px;
359350
border: 1px solid var(--border-color);
360-
border-radius: 6px;
351+
border-radius: 8px;
361352
background-color: var(--editor-bg);
362353
color: var(--text-color);
363354
outline: none;
364-
}
365-
.share-add-space-section button {
366-
padding: 5px 12px;
367-
font-size: 13px;
368-
font-weight: 600;
369-
border: 1px solid var(--accent-color);
370-
border-radius: 6px;
371-
background-color: rgba(88, 166, 255, 0.1);
372-
color: var(--accent-color);
355+
transition: border-color 0.15s;
373356
cursor: pointer;
374-
transition: all 0.15s;
375-
white-space: nowrap;
376357
}
377-
.share-add-space-section button:hover {
378-
background-color: rgba(88, 166, 255, 0.2);
358+
.share-space-select:focus {
359+
border-color: var(--accent-color);
360+
box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.15);
379361
}
380362

381363
/* ============================================

js/cloud-share.js

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -464,9 +464,10 @@
464464
if (!hash) return;
465465
var params = new URLSearchParams(hash);
466466
var spaceSlug = params.get('space');
467+
var compactId = params.get('s');
467468

468469
// --- Space hub: #space=<slug> ---
469-
if (spaceSlug) {
470+
if (spaceSlug && !compactId) {
470471
try {
471472
M.markdownPreview.innerHTML = '<div style="padding: 40px; text-align: center; opacity: 0.6;"><i class="bi bi-collection"></i> Loading Space...</div>';
472473
M.setViewMode('preview');
@@ -894,6 +895,8 @@
894895
if (hint) hint.style.display = '';
895896
// Render previously shared versions
896897
renderSharedVersions();
898+
// Populate "Add to Space" picker in options modal
899+
if (M.populateShareSpacePicker) M.populateShareSpacePicker();
897900
}
898901

899902
/** Render the "Previously Shared" list in the share options modal */
@@ -1032,8 +1035,23 @@
10321035
if (shareResult.id) {
10331036
saveSharedVersion(shareResult.id, shareResult.url, selectedShareView, isSecureShareMode ? 'secure' : 'quick');
10341037
}
1038+
// Add to selected space (if any)
1039+
var selectedSpaceSlug = '';
1040+
var spaceSelect = document.getElementById('share-space-select');
1041+
if (spaceSelect && spaceSelect.value) {
1042+
selectedSpaceSlug = spaceSelect.value;
1043+
try {
1044+
var title = 'Untitled';
1045+
var content = M.markdownEditor.value;
1046+
var headingMatch = content.match(/^#+\s+(.+)/m);
1047+
if (headingMatch) title = headingMatch[1].trim().substring(0, 60);
1048+
await M.addItemToSpace(selectedSpaceSlug, shareResult.id, title);
1049+
} catch (spaceErr) {
1050+
console.warn('Failed to add to space:', spaceErr);
1051+
}
1052+
}
10351053
closeShareOptionsModal();
1036-
showShareResult(shareResult.url, isSecureShareMode, shareResult.isForm, shareResult.rkString);
1054+
showShareResult(shareResult.url, isSecureShareMode, shareResult.isForm, shareResult.rkString, selectedSpaceSlug);
10371055
} catch (error) {
10381056
console.error('Share failed:', error);
10391057
// Show custom name errors in the custom name error div
@@ -1054,12 +1072,16 @@
10541072
var shareResultModal = document.getElementById('share-result-modal');
10551073
var lastShareRk = '';
10561074
var lastShareRespondentUrl = '';
1057-
function showShareResult(url, isSecure, isForm, rkString) {
1058-
// For form docs, append rk to create the creator link
1075+
function showShareResult(url, isSecure, isForm, rkString, spaceSlug) {
1076+
// Build the display URL
10591077
var creatorUrl = url;
10601078
if (isForm && rkString) {
10611079
creatorUrl = url + '&rk=' + rkString;
10621080
}
1081+
// Rewrite the hash to include the space before the document ID
1082+
if (spaceSlug) {
1083+
creatorUrl = creatorUrl.replace('#s=', '#space=' + spaceSlug + '&s=');
1084+
}
10631085
document.getElementById('share-link-input').value = creatorUrl;
10641086

10651087
var desc = document.getElementById('share-result-desc');
@@ -1098,9 +1120,6 @@
10981120
}
10991121

11001122
shareResultModal.classList.add('active');
1101-
1102-
// Populate "Add to Space" picker if user has spaces
1103-
if (M.populateShareSpacePicker) M.populateShareSpacePicker();
11041123
}
11051124
M.closeShareResultModal = function () { shareResultModal.classList.remove('active'); };
11061125
document.getElementById('share-result-close').addEventListener('click', M.closeShareResultModal);
@@ -1204,8 +1223,8 @@
12041223

12051224
// Track when the share result modal opens (for time-based bot detection)
12061225
var _origShowShareResult = showShareResult;
1207-
showShareResult = function (url, isSecure, isForm, rkString) {
1208-
_origShowShareResult(url, isSecure, isForm, rkString);
1226+
showShareResult = function (url, isSecure, isForm, rkString, spaceSlug) {
1227+
_origShowShareResult(url, isSecure, isForm, rkString, spaceSlug);
12091228
emailModalOpenTime = Date.now();
12101229
};
12111230
if (emailSendBtn) emailSendBtn.addEventListener('click', async function () {

js/modal-templates.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@
9999
<small class="share-view-lock-hint">Recipients will only see the selected view and cannot switch modes.</small>
100100
</div>
101101
102+
<!-- Add to Space (pre-share) -->
103+
<div id="share-space-section" class="share-form-group" style="display:none">
104+
<label><i class="bi bi-collection me-1"></i>Add to Space</label>
105+
<select id="share-space-select" class="share-space-select">
106+
<option value="">— None —</option>
107+
</select>
108+
<small>Link will open the Space hub instead of the document directly.</small>
109+
</div>
110+
102111
<!-- Previously Shared Versions -->
103112
<div id="share-versions-section" class="share-versions-section" style="display:none">
104113
<label class="share-view-lock-label"><i class="bi bi-clock-history me-1"></i> Previously Shared</label>
@@ -179,12 +188,7 @@
179188
and .md file directly to your inbox.</small>
180189
<div id="share-email-status" class="share-email-status"></div>
181190
</div>
182-
<!-- Add to Space -->
183-
<div id="share-add-to-space" class="share-add-space-section" style="display:none">
184-
<label><i class="bi bi-collection me-1"></i>Add to Space</label>
185-
<select id="share-space-picker"></select>
186-
<button id="share-add-space-btn" class="spaces-btn-primary" style="padding:5px 12px;font-size:13px">+ Add</button>
187-
</div>
191+
188192
</div>
189193
</div>
190194
</div>

js/space-manager.js

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@
437437
row.innerHTML =
438438
'<span class="spaces-editor-item-num">' + (idx + 1) + '</span>' +
439439
'<div class="spaces-editor-item-info">' +
440-
'<span class="spaces-editor-item-title">' + escapeHtml(item.title) + '</span>' +
440+
'<a href="' + (M.SHARE_BASE_URL || 'https://textagent.github.io/') + '#space=' + escapeHtml(slug) + '&s=' + escapeHtml(item.id) + '" target="_blank" class="spaces-editor-item-title">' + escapeHtml(item.title) + '</a>' +
441441
'<span class="spaces-editor-item-time">' + timeStr + '</span>' +
442442
'</div>' +
443443
'<button class="spaces-list-btn spaces-list-delete" data-idx="' + idx + '" title="Remove"><i class="bi bi-x-lg"></i></button>';
@@ -679,29 +679,6 @@
679679
});
680680
});
681681

682-
// "Add to Space" in share result modal
683-
var shareAddBtn = document.getElementById('share-add-space-btn');
684-
if (shareAddBtn) shareAddBtn.addEventListener('click', async function () {
685-
var picker = document.getElementById('share-space-picker');
686-
if (!picker || !picker.value) return;
687-
var slug = picker.value;
688-
var shareUrl = document.getElementById('share-link-input').value;
689-
var shareId = '';
690-
var match = shareUrl.match(/#s=([^&]+)/);
691-
if (match) shareId = match[1];
692-
if (!shareId) return;
693-
694-
var title = 'Untitled';
695-
var content = M.markdownEditor.value;
696-
var headingMatch = content.match(/^#+\s+(.+)/m);
697-
if (headingMatch) title = headingMatch[1].trim().substring(0, 60);
698-
699-
try {
700-
await addItemToSpace(slug, shareId, title);
701-
} catch (e) {
702-
if (M.showToast) M.showToast('Failed: ' + e.message, 'error');
703-
}
704-
});
705682

706683
// Escape to close
707684
document.addEventListener('keydown', function (e) {
@@ -712,10 +689,10 @@
712689
});
713690
}
714691

715-
// --- Populate "Add to Space" picker in Share Result ---
692+
// --- Populate "Add to Space" picker in Share Options ---
716693
function populateShareSpacePicker() {
717-
var section = document.getElementById('share-add-to-space');
718-
var picker = document.getElementById('share-space-picker');
694+
var section = document.getElementById('share-space-section');
695+
var picker = document.getElementById('share-space-select');
719696
if (!section || !picker) return;
720697

721698
var spaces = getMySpaces();
@@ -725,7 +702,7 @@
725702
return;
726703
}
727704

728-
picker.innerHTML = '';
705+
picker.innerHTML = '<option value="">\u2014 None \u2014</option>';
729706
slugs.forEach(function (slug) {
730707
var opt = document.createElement('option');
731708
opt.value = slug;

0 commit comments

Comments
 (0)