Skip to content

Commit 0c2c1ab

Browse files
committed
Added a punctuation panel and feature to store page state
1 parent fe4d109 commit 0c2c1ab

13 files changed

Lines changed: 394 additions & 136 deletions

README.hi.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
- इसी कंप्यूटर/फ़ोन पर दस्तावेज़ों का **इतिहास** रखें
1515
- ब्राउज़र से **प्रिंट** या **PDF सेव** करें
1616
- वैकल्पिक **Google Drive बैकअप** (जब आप खुद कनेक्ट और सिंक करें)
17-
- कंप्यूटर पर: **Hinglish सुझाव** और वैकल्पिक **बोलकर लिखें** माइक
17+
- कंप्यूटर पर: **Hinglish सुझाव**, **विराम चिह्न पैनल (Punctuation Panel)** और वैकल्पिक **बोलकर लिखें** माइक
1818
- फ़ोन पर: **कीबोर्ड** (और उसका माइक) — ऐप का माइक जानबूझकर छिपा रहता है
1919

2020
## कैसे खोलें

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
- Keep a **history** of documents on this computer or phone
1515
- **Print** or **Save as PDF** from your browser
1616
- Optionally **back up to Google Drive** (only when you connect and sync)
17-
- On a computer: **Hinglish suggestions** and an optional **speak-to-type** microphone
17+
- On a computer: **Hinglish suggestions**, **Punctuation Panel**, and an optional **speak-to-type** microphone
1818
- On a phone: use the **keyboard** (including its mic) — the in-app mic is hidden on purpose
1919

2020
## How to open it

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Architecture and how the pieces fit together. For everyday use (opening the app,
1818
| [Local storage](components/storage.md) | Autosave in IndexedDB |
1919
| [Drive backup](components/drive-backup.md) | Optional Google Drive sync |
2020
| [Hinglish typing](components/typing.md) | Transliteration suggestions |
21+
| Punctuation Panel | Draggable panel with common Hindi/English punctuation |
2122
| [Dictation](components/dictation.md) | In-app microphone (desktop/tablet) |
2223
| [Page preview](components/page-preview.md) | On-screen fit / pinch zoom |
2324
| [Deploy](components/deploy.md) | GitHub Pages and local preview |

editor/css/overlays-responsive.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
/* Mobile keyboards already expose a mic — hide in-app dictation chrome */
4747
@media (max-width: 768px) {
4848
.dictation-fab,
49-
.dictation-interim {
49+
.dictation-interim,
50+
.punctuation-panel {
5051
display: none !important;
5152
}
5253

editor/css/style.css

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2042,6 +2042,153 @@ span.diary-dotted-flow:focus {
20422042
}
20432043
}
20442044

2045+
/* ── Punctuation Panel ── */
2046+
.punctuation-panel {
2047+
position: fixed;
2048+
right: 16px;
2049+
top: 50%;
2050+
transform: translateY(-50%) translateX(calc(100% + 16px));
2051+
transition: transform 0.3s cubic-bezier(.4, 0, .2, 1);
2052+
z-index: 1050;
2053+
display: flex;
2054+
align-items: center;
2055+
}
2056+
2057+
.punctuation-panel.is-open {
2058+
transform: translateY(-50%) translateX(0);
2059+
}
2060+
2061+
.punctuation-toggle {
2062+
position: absolute;
2063+
left: -48px;
2064+
width: 40px;
2065+
height: 40px;
2066+
background: var(--brand-white);
2067+
border: 1px solid var(--color-border-light);
2068+
border-radius: 50%;
2069+
display: flex;
2070+
align-items: center;
2071+
justify-content: center;
2072+
cursor: pointer;
2073+
box-shadow: var(--shadow-medium);
2074+
color: var(--color-text-secondary);
2075+
font-size: 1rem;
2076+
transition: opacity 0.2s, color 0.2s, background 0.2s;
2077+
}
2078+
2079+
.punctuation-toggle:hover {
2080+
color: var(--brand-primary);
2081+
background: var(--color-background-soft);
2082+
}
2083+
2084+
.punctuation-panel.is-open .punctuation-toggle {
2085+
opacity: 0;
2086+
pointer-events: none;
2087+
transform: scale(0.9);
2088+
}
2089+
2090+
.punctuation-content {
2091+
background: var(--brand-white);
2092+
border: 1px solid var(--color-border-light);
2093+
border-radius: 12px;
2094+
padding: 12px;
2095+
box-shadow: var(--shadow-popup);
2096+
width: fit-content;
2097+
max-width: 260px;
2098+
display: flex;
2099+
flex-direction: column;
2100+
gap: 8px;
2101+
}
2102+
2103+
.punctuation-footer {
2104+
display: flex;
2105+
align-items: center;
2106+
gap: 10px;
2107+
min-height: 24px;
2108+
padding-top: 8px;
2109+
border-top: 1px solid var(--color-border-light);
2110+
}
2111+
2112+
.punctuation-close {
2113+
background: var(--brand-white);
2114+
border: 1px solid var(--color-border-medium);
2115+
box-shadow: 0 1px 3px rgba(0,0,0,0.08);
2116+
cursor: pointer;
2117+
color: var(--color-text-primary);
2118+
font-size: 0.85rem;
2119+
padding: 4px 8px;
2120+
border-radius: 6px;
2121+
flex: 0 0 auto;
2122+
line-height: 1;
2123+
transition: all 0.15s ease;
2124+
}
2125+
2126+
.punctuation-close:hover {
2127+
color: var(--color-error);
2128+
border-color: rgba(220, 53, 69, 0.4);
2129+
background: #fffafa;
2130+
box-shadow: 0 2px 4px rgba(220, 53, 69, 0.1);
2131+
}
2132+
2133+
.punctuation-close:active {
2134+
transform: translateY(1px);
2135+
box-shadow: none;
2136+
}
2137+
2138+
.punctuation-grid {
2139+
display: grid;
2140+
grid-template-columns: repeat(5, 38px);
2141+
grid-auto-rows: 38px;
2142+
gap: 4px;
2143+
justify-content: center;
2144+
}
2145+
2146+
.punctuation-tile {
2147+
display: flex;
2148+
align-items: center;
2149+
justify-content: center;
2150+
border: none;
2151+
border-radius: 6px;
2152+
background: transparent;
2153+
cursor: pointer;
2154+
font-size: 1.1rem;
2155+
color: var(--color-text-primary);
2156+
transition: background 0.15s, color 0.15s, transform 0.1s;
2157+
user-select: none;
2158+
overflow: hidden;
2159+
white-space: nowrap;
2160+
}
2161+
2162+
.punctuation-tile:hover {
2163+
background: #e9ecef;
2164+
color: var(--color-text-primary);
2165+
transform: translateY(-2px) scale(1.05);
2166+
box-shadow: 0 2px 5px rgba(0,0,0,0.06);
2167+
}
2168+
2169+
.punctuation-tile:active {
2170+
background: #dee2e6;
2171+
transform: translateY(0) scale(0.95);
2172+
box-shadow: none;
2173+
}
2174+
2175+
.punctuation-tile.is-copied {
2176+
background: var(--brand-primary);
2177+
color: var(--brand-white);
2178+
}
2179+
2180+
.punctuation-tooltip {
2181+
flex: 1 1 auto;
2182+
text-align: left;
2183+
font-size: 0.75rem;
2184+
line-height: 1.25;
2185+
color: var(--color-text-secondary);
2186+
min-height: 0;
2187+
display: flex;
2188+
align-items: center;
2189+
justify-content: flex-start;
2190+
}
2191+
20452192
@media print {
20462193
.modal-overlay {
20472194
position: static;

editor/help.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ <h3>Your rights (GDPR)</h3>
370370
</p>
371371
<ul>
372372
<li><strong>Letter</strong> and <strong>Diary</strong> templates with print-ready layout</li>
373+
<li><strong>Punctuation panel</strong> for quick insertion of standard marks</li>
373374
<li>Document history stored locally in your browser (IndexedDB)</li>
374375
<li>
375376
Optional Google Drive backup — <strong>Sync all</strong> (pull + push) or

editor/index.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,21 @@ <h3>History</h3>
283283
<div class="dictation-sheet-body"></div>
284284
</dialog>
285285

286+
<aside id="punctuationPanel" class="punctuation-panel">
287+
<button id="punctuationToggle" class="punctuation-toggle" title="विराम चिह्न (Punctuation)">
288+
<i class="fas fa-quote-right"></i>
289+
</button>
290+
<div class="punctuation-content">
291+
<div id="punctuationGrid" class="punctuation-grid"></div>
292+
<div class="punctuation-footer">
293+
<button id="punctuationClose" class="punctuation-close" title="Close" aria-label="Close">
294+
<i class="fas fa-times"></i>
295+
</button>
296+
<div id="punctuationTooltip" class="punctuation-tooltip">Hover over a mark</div>
297+
</div>
298+
</div>
299+
</aside>
300+
286301
<div id="suggestions" class="suggestions-box"></div>
287302
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.js"></script>
288303
<script type="module" src="js/main.js"></script>

editor/js/drive-sync.js

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -281,58 +281,6 @@ async function uploadDocFile(doc, folderId, fileId) {
281281
return createMultipart();
282282
}
283283

284-
/**
285-
* Push one document (live or tombstone).
286-
* @param {'letter'|'diary'} type
287-
* @param {number} id
288-
*/
289-
export async function pushDocById(type, id) {
290-
return enqueue(async () => {
291-
if (!isConnected()) return { ok: false, reason: 'disconnected' };
292-
setSyncState('syncing');
293-
try {
294-
const doc = await getDocumentById(type, id);
295-
if (!doc || !doc.uuid) {
296-
setSyncState('idle');
297-
return { ok: false, reason: 'missing' };
298-
}
299-
if (!needsBackup(doc)) {
300-
setSyncState('idle');
301-
return { ok: true, skipped: true };
302-
}
303-
304-
const folderId = await ensureFolder();
305-
let fileId = doc.driveFileId || null;
306-
if (!fileId) {
307-
const found = await findFileByUuid(folderId, doc.uuid);
308-
fileId = found?.id || null;
309-
}
310-
311-
const newId = await uploadDocFile(doc, folderId, fileId);
312-
const syncedAt = new Date().toISOString();
313-
await markSynced(type, id, {
314-
driveFileId: newId,
315-
syncedAt,
316-
syncError: null,
317-
});
318-
319-
if (doc.deletedAt) {
320-
await hardDeleteById(type, id);
321-
}
322-
323-
setSyncState('idle');
324-
return { ok: true, driveFileId: newId };
325-
} catch (err) {
326-
const message = err?.message || String(err);
327-
try {
328-
await markSynced(type, id, { syncError: message });
329-
} catch (_) { /* ignore */ }
330-
setSyncState('error', message);
331-
return { ok: false, error: message };
332-
}
333-
});
334-
}
335-
336284
export async function pushPending(type) {
337285
return enqueue(async () => {
338286
if (!isConnected()) return { ok: false, reason: 'disconnected' };

0 commit comments

Comments
 (0)