diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000..1c66314 --- /dev/null +++ b/README.en.md @@ -0,0 +1,63 @@ +
+
+
Talk with yourself. Make room for an inner reply.
+中文 · English
+ + +ConversationsWithGod is a quiet space for self-dialogue. On the right, write a question, a feeling or a thought. On the left, take another perspective and answer with your own inner wisdom. + +You write both sides. “God” can mean the divine, your higher self, or simply a clearer, kinder voice within. Give that voice a little room; an answer does not have to arrive immediately. + +
+
+
+
+
-
+
- "Meaning is not something we find, but something we create." — Alfred Adler -
- -## 🚀 Getting Started - -No installation required! +和自己说说话,听见内心的回应。
+中文 · English
+ -1. Visit [https://studentiz.github.io/ConversationsWithGod/](https://studentiz.github.io/ConversationsWithGod/) -2. Start your conversation -3. Alternate between you (the seeker/student) and your inner wisdom (God) +ConversationsWithGod 是一个安静的自我对话空间。右侧写下你的困惑、念头和提问;左侧,换一个角度,用自己的内在智慧回应。 -Or download the HTML file and open it locally in any browser. - -## 🪞 A Mirror for Your Soul - -ConversationsWithGod is a simple but profound tool for having meaningful conversations with yourself. Frame your thoughts as a dialogue between a seeker and the divine – or however you choose to imagine your higher self. - -Unlike traditional journal apps or AI chatbots, this is a space for genuine self-reflection. You play both parts in the conversation, allowing your own wisdom to emerge through the dialogue. +你扮演对话中的双方。这里的“God”可以是你理解的神、理想中的自己,或心底那个更清醒、更温柔的声音。留一点时间给它,答案不必马上出现。
-
+
-
-
-
-
-
+
- "It is not what happens to us that determines our lives, but how we interpret and respond to it." — Alfred Adler -
+对话保存在当前浏览器中,没有账号、同步、分析追踪或上传对话的服务。清理浏览器数据可能会移除历史;需要长期留存的内容,可以导出备份。 -- Made with ❤️ for seekers everywhere -
+## 参与项目 + +欢迎提出问题、改进建议或贡献代码。构建与验证方式见 [开发说明](docs/DEVELOPMENT.md),已有检查见 [验证记录](docs/VALIDATION.md)。 + +如果这个小空间对你有所帮助,也欢迎把它分享给需要的人。 diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 174b355..c782d0d 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -11,7 +11,7 @@ npm run test:motion npm run test:browser ``` -`web/index.template.html` contains the original paper UI and interaction code. `web/storage.js`, `web/transcript.js` and `web/exports.js` are inlined by the build into the standalone root `index.html`. GitHub Pages serves that generated file from `main` as before. +`web/index.template.html` contains the original paper UI and interaction code. `web/storage.js`, `web/transcript.js`, `web/i18n.js` and `web/exports.js` are inlined by the build into the standalone root `index.html`. GitHub Pages serves that generated file from `main` as before. The public build is a single standalone HTML file plus optional PWA assets. `npm run build:icons` regenerates the web icons and README logo from `icons/mark.svg`, using the same borderless dialogue-bubble shapes and paper/gray/green palette as the interface. @@ -23,6 +23,10 @@ Pages migrates the previous `chat_messages` localStorage array into IndexedDB at The transcript uses variable-height virtual rows with overscan, measured height caching, binary-search lookup and explicit scroll anchoring. Native browser anchoring is disabled to avoid double adjustment when spacers change. Every message remains in the history and is reachable by continuous scrolling; exporting reads the full history, not the rendered DOM. -Only a newly sent short message gets a character-opacity reveal. Grapheme segmentation preserves composed emoji and text, spans are capped at 160. A single character fades in over 600 ms; phrases have at least a 700 ms span between the first and last character starts, with a 420 ms fade for each. Longer passages use approximately 65 ms between starts, capped at a six-second stagger, and collapse back to one text node within 6.52 seconds. Long messages render immediately. Menus and dialogs use short opacity / transform transitions. The reduced-motion preference removes reveals and transitions; hidden documents stop maintenance timers and finish active text reveals. Idle conversations have no continuous decorative animation. Aging is applied to a shared content wrapper so the body and timestamp fade and blur together, while deletion controls stay clear. There is no added fading toggle or backup import item. +Text is inserted in full as one text node. Sending applies a 240 ms upward transform to the new bubble and to visible earlier bubbles displaced by it, with at most 24 moving rows. Scrolling cancels message movement; hidden views and reduced-motion changes finish it immediately. Historical rows do not replay entry animation. + +The conversation keeps native wheel, touch and keyboard scrolling with a custom translucent overlay handle. It is hidden when there is no overflow, appears immediately during scrolling or interaction near the right edge, waits 800 ms after activity, then fades over 600 ms. Dragging, track clicks, Home/End and arrow/page keys are supported. ResizeObserver and one scheduled animation frame update its geometry; there is no polling or permanent decorative animation. Native scrollbar visibility settings cannot reveal a second track. The composer auto-grows to a viewport-limited height and allows caret scrolling without showing a scrollbar; width changes remeasure wrapped drafts. + +Aging is applied to a shared content wrapper so the body and timestamp fade and blur together, while deletion controls stay clear. Language follows the browser; Chinese and English documentation screenshots are captured from isolated example histories with `npm run screenshots`. These choices follow [Apple's motion guidance](https://developer.apple.com/design/human-interface-guidelines/motion): purposeful, brief feedback, input available during animation, and accessibility preferences. Smoothness and power are tested separately from correctness; there is no measured battery-life claim. diff --git a/docs/VALIDATION.md b/docs/VALIDATION.md index 7f17dbe..f4b55e0 100644 --- a/docs/VALIDATION.md +++ b/docs/VALIDATION.md @@ -9,11 +9,17 @@ Local hardware: Apple M4 Pro, macOS 15.8. Chrome runs headlessly at 390 × 844 f Opening measurements include the new version's initial migration. Send timing measures click-to-next-animation-frame; it is not disk durability latency. The data is one local run with three sends per condition, rather than a cross-device performance guarantee. In the 90-frame scripted scroll probe, both versions recorded zero intervals above 34 ms; after-change p95 intervals were 16.7 ms in both sizes. This probe alone does not establish smoothness of every animation. Run `npm run benchmark` to reproduce; raw results go into `.test-results/performance.json`. -Browser integration covers 100, 3,000 and 10,000 messages: complete migration and reload, continuous top/middle/end history access, bounded row count, stable-ID deletion and undo, full-history download, composition input, multiline input, bounded character animation and cleanup, reduced motion, quota failure recovery, corrupt-history preservation, offline reopening, standalone HTML, font changes and fading. Notification rectangles remain below the header and above the composer in portrait and small landscape layouts. The normal conversation has no persistent save/status footer, connection messages, font-change messages or automatic installation banner. Saving starts with a readable copy; backup and model settings appear only when selected. A storage failure offers a working Save a copy action, opening a damaged history can be retried. Browser tests exercise these controls and verify continuation and training exports while offline, without network requests after initial page loading. +Browser integration covers 100, 3,000 and 10,000 messages: complete migration and reload, continuous top/middle/end history access, bounded row count, stable-ID deletion and undo, full-history download, composition input, multiline input, complete-text insertion and bounded whole-bubble movement, reduced motion, quota failure recovery, corrupt-history preservation, offline reopening, standalone HTML, font changes and fading. Notification rectangles remain below the header and above the composer in portrait and small landscape layouts. The normal conversation has no persistent save/status footer, connection messages, font-change messages or automatic installation banner. Saving starts with a readable copy; backup and model settings appear only when selected. A storage failure offers a working Save a copy action, opening a damaged history can be retried. Browser tests exercise these controls and verify continuation and training exports while offline, without network requests after initial page loading. Export-format tests cover backup fidelity, provider-specific request fields, training target weights, incomplete turns, adjacent-role merging, Unicode text and the retained legacy formats. -API requests and fine-tuning files are validated against the linked schemas and local tests. No paid model calls or training jobs were performed. Battery consumption has not been measured; the implementation bounds rendering and character spans, stops maintenance when hidden and avoids continuous idle animation. +API requests and fine-tuning files are validated against the linked schemas and local tests. No paid model calls or training jobs were performed. Battery consumption has not been measured; the implementation bounds rendering and animated rows, stops maintenance when hidden and avoids continuous idle animation. -The timestamp regression first failed with a clear timestamp beside blurred text; after the fix the inherited blur is identical for both, with the delete control unaffected. A three-character Chinese phrase previously reached 0.96 opacity on the last character after 210 ms; it now still has its last character at zero opacity then, completes within the bounded reveal time and retains its exact text. These focused regressions run with `npm run test:motion`. Browser integration also checks compact 320 × 400 / 320 × 568, landscape, tablet and desktop dimensions, complete reveal cleanup, reduced motion, and the removal of the two unrequested menu items. +The current UI regressions run with `npm run test:motion`. A 278 × 497 draft with seven lines previously had `overflow-y: auto`, a 118 px viewport and 188 px of content, exposing a native scrollbar. The revised composer uses hidden overflow, grows within the viewport and preserves all multiline input. Tests edit the end of long drafts at 278 × 497, 320 × 400, 320 × 568 and 844 × 390. Width changes remeasure wrapping on the next animation frame, avoiding mutation during ResizeObserver delivery. + +New messages contain their entire Unicode and multiline text immediately in one text node. A whole-bubble transform lasts 240 ms; reduced motion skips it. Old messages do not replay entry motion during virtual scrolling. Timestamp and text still share their blur layer, while deletion controls remain clear. + +The custom conversation scrollbar is invisible at rest and absent without overflow. Tests observe opacity 1 during scrolling, an intermediate opacity during the 600 ms fade, and 0 after it finishes. They exercise actual pointer dragging, Home/End keyboard navigation and ARIA position updates. The browser retains native wheel and touch scrolling. Chinese and English UI labels, purpose-based export actions, bubble colors and matching documentation screenshots are checked. No added fading or backup-import menu items are present. + +Browser integration was rerun with 100, 3,000 and 10,000 records after this change, including save/reload, complete exports, deletion/undo, offline reopening and narrow/landscape layouts. The 10,000-record case kept 10 live message rows. Opening timings above describe the earlier baseline comparison, not a new cross-device benchmark. diff --git a/icons/apple-touch-icon.png b/icons/apple-touch-icon.png index 9c2540e..6a522c0 100644 Binary files a/icons/apple-touch-icon.png and b/icons/apple-touch-icon.png differ diff --git a/icons/favicon-16x16.png b/icons/favicon-16x16.png index 34beeb1..8820a94 100644 Binary files a/icons/favicon-16x16.png and b/icons/favicon-16x16.png differ diff --git a/icons/favicon-32x32.png b/icons/favicon-32x32.png index 9348078..b3c5ffe 100644 Binary files a/icons/favicon-32x32.png and b/icons/favicon-32x32.png differ diff --git a/icons/icon-192x192.png b/icons/icon-192x192.png index e06238c..de61aa1 100644 Binary files a/icons/icon-192x192.png and b/icons/icon-192x192.png differ diff --git a/icons/icon-512x512.png b/icons/icon-512x512.png index 03f59f4..0e92605 100644 Binary files a/icons/icon-512x512.png and b/icons/icon-512x512.png differ diff --git a/icons/mark.svg b/icons/mark.svg index 5926a2a..bf2e087 100644 --- a/icons/mark.svg +++ b/icons/mark.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/icons/maskable-icon.png b/icons/maskable-icon.png index 7301856..2e8b669 100644 Binary files a/icons/maskable-icon.png and b/icons/maskable-icon.png differ diff --git a/img/en/conversation.png b/img/en/conversation.png new file mode 100644 index 0000000..3c62ee4 Binary files /dev/null and b/img/en/conversation.png differ diff --git a/img/en/dark.png b/img/en/dark.png new file mode 100644 index 0000000..f08e155 Binary files /dev/null and b/img/en/dark.png differ diff --git a/img/en/export.png b/img/en/export.png new file mode 100644 index 0000000..c197f71 Binary files /dev/null and b/img/en/export.png differ diff --git a/img/export-formats1.png b/img/export-formats1.png deleted file mode 100644 index e9ec942..0000000 Binary files a/img/export-formats1.png and /dev/null differ diff --git a/img/export-formats2.png b/img/export-formats2.png deleted file mode 100644 index 65ccf26..0000000 Binary files a/img/export-formats2.png and /dev/null differ diff --git a/img/logo.png b/img/logo.png index 3251a3d..fde0894 100644 Binary files a/img/logo.png and b/img/logo.png differ diff --git a/img/privacy-blur.png b/img/privacy-blur.png index d95f3f0..f11eea2 100644 Binary files a/img/privacy-blur.png and b/img/privacy-blur.png differ diff --git a/img/screenshot.png b/img/screenshot.png index cce98ad..7c7c7c9 100644 Binary files a/img/screenshot.png and b/img/screenshot.png differ diff --git a/img/zh/conversation.png b/img/zh/conversation.png new file mode 100644 index 0000000..83d4d93 Binary files /dev/null and b/img/zh/conversation.png differ diff --git a/img/zh/dark.png b/img/zh/dark.png new file mode 100644 index 0000000..e76237e Binary files /dev/null and b/img/zh/dark.png differ diff --git a/img/zh/export.png b/img/zh/export.png new file mode 100644 index 0000000..b083f44 Binary files /dev/null and b/img/zh/export.png differ diff --git a/index.html b/index.html index 6e22a32..ac0435d 100644 --- a/index.html +++ b/index.html @@ -51,8 +51,8 @@ --header-background: #f1ebe3; --input-area-background: #f1ebe3; --text-color: #4a4a4a; - --own-message-background: #e6f3e6; - --other-message-background: #f0f0f0; + --own-message-background: #eeebe6; + --other-message-background: #dce7dc; --input-background: #ffffff; --button-color: #7c9c81; --button-text: white; @@ -81,8 +81,8 @@ --header-background: #211e1a; --input-area-background: #211e1a; --text-color: #e8d9c5; - --own-message-background: #3d3426; - --other-message-background: #322b21; + --own-message-background: #39342e; + --other-message-background: #35463b; --input-background: #352e25; --button-color: #7c6e56; --button-text: #f0e6d9; @@ -162,7 +162,7 @@ word-wrap: break-word; -webkit-hyphens: auto; -ms-hyphens: auto; - hyphens: auto; + hyphens: manual; overflow-wrap: break-word; } @@ -991,11 +991,20 @@ } } - /* Preserve the original palette, typography and bubble shapes. */ + /* Paper, warm grey and sage; the reply is the visual focus. */ body { height: 100dvh; } .chat-container { height: 100%; } .chat-header .title { font-family: 'Brush Script MT', 'Segoe Script', cursive; } .chat-messages { display: block; scroll-behavior: auto; min-height: 0; overflow-anchor: none; } + .transcript-shell { flex: 1; min-height: 0; position: relative; overflow: hidden; } + .transcript-shell .chat-messages { height: 100%; overflow-x: hidden; scrollbar-width: none; } + .chat-messages::-webkit-scrollbar, #messageInput::-webkit-scrollbar { display: none; width: 0; height: 0; } + .scroll-indicator { position: absolute; top: 8px; bottom: 8px; right: 2px; width: 18px; opacity: 0; transition: opacity .6s ease-out; pointer-events: none; touch-action: none; border-radius: 10px; outline-offset: -2px; } + .scroll-indicator.visible, .scroll-indicator:focus-visible { opacity: 1; transition: none; pointer-events: auto; } + .scroll-thumb { position: absolute; width: 5px; right: 4px; top: 0; border-radius: 4px; background: rgba(74, 74, 74, .32); } + [data-theme="dark"] .scroll-thumb { background: rgba(248, 245, 241, .34); } + .scroll-indicator:hover .scroll-thumb, .scroll-indicator:focus-visible .scroll-thumb { background: rgba(74, 74, 74, .5); } + [data-theme="dark"] .scroll-indicator:hover .scroll-thumb, [data-theme="dark"] .scroll-indicator:focus-visible .scroll-thumb { background: rgba(248, 245, 241, .52); } .message-window { display: flex; flex-direction: column; gap: 10px; padding-bottom: 10px; } .message { animation: none; transition: none; } .message-text { white-space: pre-wrap; } @@ -1007,7 +1016,7 @@ .notification span { flex: 1; overflow-wrap: anywhere; } .notification button { border: 0; background: none; font: inherit; color: var(--button-color); cursor: pointer; flex-shrink: 0; } .notification .notice-dismiss { color: var(--text-color); font-size: 20px; } - #messageInput { font-family: inherit; resize: none; max-height: 120px; line-height: 1.5; } + #messageInput { font-family: inherit; resize: none; max-height: min(160px, 32dvh); line-height: 1.5; overflow: hidden; scrollbar-width: none; } #sendButton:disabled { opacity: .45; cursor: default; transform: none; } .chat-input-area { align-items: flex-end; padding-bottom: max(12px,env(safe-area-inset-bottom)); } .export-field { display: flex; flex-direction: column; gap: 6px; margin: 14px 0; font-size: 13px; } @@ -1029,12 +1038,10 @@ .modal-dialog { max-height: 88dvh; } .modal-body { overflow-y: auto; min-height: 0; } .sr-only { position: absolute; width: 1px; height: 1px; clip-path: inset(50%); overflow: hidden; } - .revealed-glyph { animation: gentleReveal .42s cubic-bezier(.2,.8,.2,1) both; } - @keyframes gentleReveal { from { opacity: 0; } to { opacity: 1; } } .menu-dropdown { transition: opacity .18s ease-out, transform .22s cubic-bezier(.2,.8,.2,1), visibility .18s; } #sendButton { transition: background-color .18s, transform .16s cubic-bezier(.2,.8,.2,1), opacity .18s; } .modal-overlay { transition: opacity .2s ease-out, visibility .2s; } - @media (prefers-reduced-motion: reduce) { .revealed-glyph { animation: none; } * { scroll-behavior: auto !important; } } + @media (prefers-reduced-motion: reduce) { * { scroll-behavior: auto !important; } } @@ -1087,14 +1094,16 @@ -