From d1c18ccbbace48507d34140f6b187537ecfdac42 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 11:58:50 +0000 Subject: [PATCH 1/5] Initial plan From 6df68371a82b850eb4c226ff2463841d48fc92ca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 12:01:53 +0000 Subject: [PATCH 2/5] Fix clipboard handwriting canvas height and improve memory page layout - Fixed canvas height issue in handwriting mode by adding flex:1 and min-height to .clipboard-drawing-wrap - Changed canvas height from auto to 100% to properly fill container - Restructured memory page to use 2-column desktop layout - Added responsive mobile layout (single column) for memory page - Added Chinese translations for new memory page strings - Compiled updated translations Co-authored-by: w1010tdev <246258262+w1010tdev@users.noreply.github.com> --- static/css/style.css | 30 ++++++++++++++++++++---- templates/memories.html | 4 ++++ translations/zh/LC_MESSAGES/messages.mo | Bin 5568 -> 6994 bytes translations/zh/LC_MESSAGES/messages.po | 8 +++++++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/static/css/style.css b/static/css/style.css index 87c8cc8..0d8a7d2 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -449,11 +449,13 @@ body { border-radius: var(--border-radius); background: #ffffff; padding: 0.5rem; + flex: 1; + min-height: 300px; } .clipboard-drawing-wrap canvas { width: 100%; - height: auto; + height: 100%; border-radius: var(--border-radius); background: #ffffff; display: block; @@ -600,11 +602,16 @@ body { .memory-page { margin-top: 1rem; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1.5rem; } .memory-pane.standalone-memory { box-shadow: 0 2px 10px rgba(30, 64, 175, 0.12); border-left-color: #4f46e5; + padding: 1rem; + border-radius: var(--border-radius); } .toolbar-btn.link-btn { @@ -660,8 +667,20 @@ body { display: flex; flex-direction: column; gap: 0.5rem; - border-top: 1px solid #e2e8f0; - padding-top: 0.75rem; + margin-top: 0.75rem; +} + +.memory-pane h3 { + font-size: 1.1rem; + color: #1f2937; + margin-bottom: 0.75rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.memory-pane h3 i { + color: #4f46e5; } .memory-form input, @@ -1093,7 +1112,8 @@ body { } .memory-pane { - display: none; + display: flex; + flex-direction: column; } .memory-pane.is-open { @@ -1106,6 +1126,8 @@ body { .memory-page { margin-top: 0.5rem; + grid-template-columns: 1fr; + gap: 1rem; } .memory-list { diff --git a/templates/memories.html b/templates/memories.html index 17fff1d..db53f9c 100644 --- a/templates/memories.html +++ b/templates/memories.html @@ -13,6 +13,7 @@

{{ _('Memories with %(username)s', username
+

{{ _('Memory List') }}

{% if memories %} {% for memory in memories %} @@ -45,6 +46,9 @@

{{ _('Memories with %(username)s', username

{{ _('No memories yet. Add one below!') }}

{% endif %}

+
+
+

{{ _('Add New Memory') }}

diff --git a/translations/zh/LC_MESSAGES/messages.mo b/translations/zh/LC_MESSAGES/messages.mo index cd71548753c29fbef8c00ace5d7f0abf00a22758..e04dc5d2aa71207491c56ef8489bcd9797acbf46 100644 GIT binary patch delta 3097 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 diff --git a/translations/zh/LC_MESSAGES/messages.po b/translations/zh/LC_MESSAGES/messages.po index 054fc63..b50556b 100644 --- a/translations/zh/LC_MESSAGES/messages.po +++ b/translations/zh/LC_MESSAGES/messages.po @@ -336,6 +336,14 @@ msgstr "还没有回忆,下面添加一个吧!" msgid "Date" msgstr "日期" +#: templates/memories.html:16 +msgid "Memory List" +msgstr "回忆列表" + +#: templates/memories.html:42 +msgid "Add New Memory" +msgstr "添加新回忆" + #: templates/memories.html:51 msgid "Memory" msgstr "回忆" From cb3376e12e20af850cc16583271ab06b4b1fb590 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 12:51:16 +0000 Subject: [PATCH 3/5] Fix canvas display mode and add comprehensive Chinese translations - Changed .clipboard-drawing-wrap display from 'block' to 'flex' to work with flexbox parent - Wrapped all error messages in app.py with _() for translation support - Added Chinese translations for all error messages (Invalid credentials, Connection not found, Access denied, etc.) - Compiled updated translations Co-authored-by: w1010tdev <246258262+w1010tdev@users.noreply.github.com> --- app.py | 46 +++++++++---------- static/css/style.css | 2 +- translations/zh/LC_MESSAGES/messages.mo | Bin 6994 -> 7733 bytes translations/zh/LC_MESSAGES/messages.po | 57 ++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 24 deletions(-) diff --git a/app.py b/app.py index c811926..e6e758a 100644 --- a/app.py +++ b/app.py @@ -203,7 +203,7 @@ def login(): login_user(user) return redirect(url_for('dashboard')) - return render_template('login.html', error='Invalid credentials') + return render_template('login.html', error=_('Invalid credentials')) return render_template('login.html') @@ -239,17 +239,17 @@ def refresh_token(): def add_memory(connection_id): connection = Connection.get_by_id(connection_id) if not connection or not connection.involves_user(current_user.id): - return jsonify({'success': False, 'error': 'Connection not found'}), 404 + return jsonify({'success': False, 'error': _('Connection not found')}), 404 data = request.get_json(silent=True) or {} memory_text = (data.get('memory_text') or '').strip() memory_date = (data.get('memory_date') or '').strip() if not memory_text or len(memory_text) > 200: - return jsonify({'success': False, 'error': 'Invalid memory text'}), 400 + return jsonify({'success': False, 'error': _('Invalid memory text')}), 400 if not validate_memory_date(memory_date): - return jsonify({'success': False, 'error': 'Invalid memory date'}), 400 + return jsonify({'success': False, 'error': _('Invalid memory date')}), 400 memory_id = ConnectionMemory.create(connection_id, current_user.id, memory_text, memory_date) return jsonify({'success': True, 'memory_id': memory_id}) @@ -260,10 +260,10 @@ def add_memory(connection_id): def approve_memory(connection_id, memory_id): connection = Connection.get_by_id(connection_id) if not connection or not connection.involves_user(current_user.id): - return jsonify({'success': False, 'error': 'Connection not found'}), 404 + return jsonify({'success': False, 'error': _('Connection not found')}), 404 if not ConnectionMemory.approve(connection_id, memory_id, current_user.id): - return jsonify({'success': False, 'error': 'Unable to approve memory'}), 400 + return jsonify({'success': False, 'error': _('Unable to approve memory')}), 400 return jsonify({'success': True}) @@ -293,7 +293,7 @@ def connection_page(connection_id): connection = Connection.get_by_id(connection_id) if not connection or not connection.involves_user(current_user.id): - return render_template('error.html', message='Connection not found') + return render_template('error.html', message=_('Connection not found')) other_user = connection.get_other_user(current_user.id) return render_template('connection.html', connection=connection, other_user=other_user) @@ -304,7 +304,7 @@ def clipboard(connection_id): connection = Connection.get_by_id(connection_id) if not connection or not connection.involves_user(current_user.id): - return render_template('error.html', message='Connection not found') + return render_template('error.html', message=_('Connection not found')) clipboard_data = SharedClipboard.get_by_connection(connection_id) other_user = connection.get_other_user(current_user.id) @@ -319,7 +319,7 @@ def clipboard(connection_id): def memories(connection_id): connection = Connection.get_by_id(connection_id) if not connection or not connection.involves_user(current_user.id): - return render_template('error.html', message='Connection not found') + return render_template('error.html', message=_('Connection not found')) other_user = connection.get_other_user(current_user.id) memories = ConnectionMemory.get_for_connection(connection_id) @@ -334,7 +334,7 @@ def drawing_game(connection_id): connection = Connection.get_by_id(connection_id) if not connection or not connection.involves_user(current_user.id): - return render_template('error.html', message='Connection not found') + return render_template('error.html', message=_('Connection not found')) game = DrawingGame.get_by_connection(connection_id) other_user = connection.get_other_user(current_user.id) @@ -350,7 +350,7 @@ def chat(connection_id): connection = Connection.get_by_id(connection_id) if not connection or not connection.involves_user(current_user.id): - return render_template('error.html', message='Connection not found') + return render_template('error.html', message=_('Connection not found')) other_user = connection.get_other_user(current_user.id) messages = ChatMessage.get_messages(connection_id) @@ -376,7 +376,7 @@ def admin_login(): if secrets.compare_digest(password, admin_password): session['is_admin'] = True return redirect(url_for('admin_panel')) - return render_template('admin_login.html', error='Invalid password') + return render_template('admin_login.html', error=_('Invalid password')) return render_template('admin_login.html') @@ -615,23 +615,23 @@ def handle_drawing_start(data): # Validate access if not validate_connection_access(connection_id, drawer_id): - return {'success': False, 'error': 'Access denied'} + return {'success': False, 'error': _('Access denied')} # Validate answer (limit to 50 characters) answer = answer.strip()[:50] if not answer: - return {'success': False, 'error': 'Answer required'} + return {'success': False, 'error': _('Answer required')} # Get or create session session = DrawingSession.get_active_session(connection_id) if not session: - return {'success': False, 'error': 'No active session'} + return {'success': False, 'error': _('No active session')} if session.waiting_for_partner: - return {'success': False, 'error': 'Waiting for partner to join'} + return {'success': False, 'error': _('Waiting for partner to join')} if session.is_session_complete(): - return {'success': False, 'error': 'Session complete'} + return {'success': False, 'error': _('Session complete')} # Start the round session.start_next_round(drawer_id, answer) @@ -721,15 +721,15 @@ def handle_end_game_early(data): session_id = data.get('session_id') if not validate_connection_access(connection_id, current_user.id): - return {'success': False, 'error': 'Access denied'} + return {'success': False, 'error': _('Access denied')} session = DrawingSession.get_by_id(session_id) if not session or session.connection_id != connection_id: - return {'success': False, 'error': 'Invalid session'} + return {'success': False, 'error': _('Invalid session')} # Ensure the session is still active if not session.is_active: - return {'success': False, 'error': 'Session is not active'} + return {'success': False, 'error': _('Session is not active')} # End the session session.end_session() @@ -749,7 +749,7 @@ def handle_create_new_game_session(data): connection_id = data.get('connection_id') if not validate_connection_access(connection_id, current_user.id): - return {'success': False, 'error': 'Access denied'} + return {'success': False, 'error': _('Access denied')} # Create new session (this will deactivate old ones) session_id = DrawingSession.create(connection_id, current_user.id, DEFAULT_ROUNDS_PER_SESSION) @@ -773,11 +773,11 @@ def handle_send_message(data): # Validate access if not validate_connection_access(connection_id, current_user.id): - return {'success': False, 'error': 'Access denied'} + return {'success': False, 'error': _('Access denied')} # Validate message length (max 1000 chars) if not message or len(message) > 1000: - return {'success': False, 'error': 'Invalid message'} + return {'success': False, 'error': _('Invalid message')} # Save message message_id = ChatMessage.create(connection_id, current_user.id, message) diff --git a/static/css/style.css b/static/css/style.css index 0d8a7d2..ef8aada 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -471,7 +471,7 @@ body { } .clipboard-display.drawing-active .clipboard-drawing-wrap { - display: block; + display: flex; } .clipboard-display.drawing-active { diff --git a/translations/zh/LC_MESSAGES/messages.mo b/translations/zh/LC_MESSAGES/messages.mo index e04dc5d2aa71207491c56ef8489bcd9797acbf46..2b7624113aeb0b3aac849f410f7a6e5b4d0f71fe 100644 GIT binary patch delta 2759 zcmZA14@}f$9LMqJ6jMY&PX$E73;dT^LQX1W)8S^*bh`d-%}mCf?o{rOJO5XwrU+>M zJIz8(%rNHyoS0@BPA#*!N^7x3&DG*}fYzqVmbtRtpYAD~4DR(j&+mD@&-eL0zvHXC z_wyr{-3i+bKV|$4$EXc2qe|a`x~i+F1aH{! z6tYnwsi-(vsEUs6N&Qvg84M^9AL``uPrxjb1=iW)617<){<9g8gt4>dIJzWUVPjmGTW#0(d(SiG@*>sYG4XKGcREq2iuF#fw~^kxkVO}{`w?@RhEDzkDuK(kf6LmP>$nb8!7Xxb>hEmKaJxkU?%FF z7>*+_A9b?1sExy@4OXD8YP}ubkAyO{sD#gA5B4`rG!*zJRDfUXgd4Wsl`OPDGJ0@; z?dPKcO~%1E6Llr4P#aaD;vKRcLshsDm0&YQ)VN|N+(IpM5k(8rteMs!sDB(IZGRMM zeI9DvB?O#Wo{3hz8U3epDnsn4lmy23o zfd5v)IurGh22od3kDAwvs`QVjgs=9e{+i%oR}G9r{XZx`E%2fen2%#{DQcrSR3fKQ z-~VRRyiU|t>rzntRNK!+RceHFEb7XpL}=vE2-*kxQD4WSsDzqPfv%wf|7A_!|EI>2 zP=V7?&xcz_p%Tlp{aL8e`%vG8HK=uw!!(A_ID^`-4RylXw%_~Sc*%xYb5R?OLw-#G zADTbgj)zc{S#FIYIh*$|2hZF7AIJfcOh3|6fUL}DRo7`rMZQqTw)Az zXo;`XQ|eo|$Y1L7HdJ;S=t>L(!=5?8MFDR^RpLQcWO86};R3(cQ^W*cAnY$(5HkOb z75j>VrOQ0t!m#h|v9NDx_>QqqsBmt_oRY#&Xi2ct`~QRxdHI6@Gu0oM?c2si{i|Y|Hn%kHY^~iE+gl!8*yqp0*sg=IibkjWP{W+Qhh34_j_TO<^46*~&br#R z`cEbmyGNa}wNBmfj?viB^4O~Sj=oc0)>?f>td^5iZS}iNd$cjO zvBo)e*m>tQr~D{6IMD}E-*v|h?~U!Mh#fiMR33;=ZMc+r(lsdF*8I8+vGUC(-q!(6 ntz>q?hV)Ne(Fqwd+^w}0PUBjq{&?*5<4$F@v$iI>DdX%v9&ORp delta 2059 zcmYMze`r-@9LMqJ%(>rgcXQtT>dtb@pQv+V^Pg>Z)yj}%7(_^Dm^P82(iZAexS}ZP zSR0*6Vk=6xD)^7{6x^I53AxHjU8R3G3>g#W4_tqU4j20Vj4yaO_q?88=leX*_dMr^ z+QFLGjlAS99KY@SU*JD9MYZR@-c;wNQ~esNun$vk1T*j+X5%9aV@jHHnHVwWq57*a z3mdI{CFVO9bE_#7($R{27lP~%dlXd#(66=$LP z=W?5Yl~{{!Vg~cOeUk;}x=|VEK?O>n7Iqek@G5G7W60$mbJMsa(x3@bQT?T;ozBKm ztilk!gE_bk=U^KaFu&`epcDtF6%U{S|AECgho#m#9E}}B| zH|iYUMy2{5Hefy{Q5$&|XJKw$*b{UqhkQmzwqF z>!=JgS$zX)Bb!kR--%lArx?>=IYvPfC2YVs)S((gr8tlGp#D14D`-S5cm=9|z16!= zpJyC3egHM`pID98Q47drb}gX1ko@cWtfQuZAE6#>wR*eN_oGtXi8Xi%mC8}nS2B)T zP=rrJajH;p8q8(Zz5?~!`_|r4ME)lyq(KX5w~p^nDL#%my#uHRAL7e6mHcYL1?DTL z%)Do=Ma6Bw2)0_i8(FJ6Y@VD)L5Jg4tiTbghiTMKDp4tmmiRl;-tzxRiwB3&i@o5- zjBmW;rj46h{F|Y$emw&^PBws{8Rq!8BcZ;PCN2C>u3pa1{> diff --git a/translations/zh/LC_MESSAGES/messages.po b/translations/zh/LC_MESSAGES/messages.po index b50556b..7bb2dc8 100644 --- a/translations/zh/LC_MESSAGES/messages.po +++ b/translations/zh/LC_MESSAGES/messages.po @@ -375,6 +375,63 @@ msgstr "无法确认回忆。" #: templates/memories.html:117 msgid "Memory approved!" msgstr "回忆已确认!" + +#: app.py:206 +msgid "Invalid credentials" +msgstr "无效的凭证" + +#: app.py:249 +msgid "Invalid memory text" +msgstr "无效的回忆内容" + +#: app.py:252 +msgid "Invalid memory date" +msgstr "无效的回忆日期" + +#: app.py:242 app.py:263 app.py:296 app.py:307 app.py:322 app.py:337 app.py:353 +msgid "Connection not found" +msgstr "未找到连接" + +#: app.py:266 +msgid "Unable to approve memory" +msgstr "无法确认回忆" + +#: app.py:379 +msgid "Invalid password" +msgstr "无效的密码" + +#: app.py:618 app.py:724 app.py:752 app.py:776 +msgid "Access denied" +msgstr "拒绝访问" + +#: app.py:623 +msgid "Answer required" +msgstr "需要提供答案" + +#: app.py:628 +msgid "No active session" +msgstr "没有活动会话" + +#: app.py:631 +msgid "Waiting for partner to join" +msgstr "等待对方加入" + +#: app.py:634 +msgid "Session complete" +msgstr "会话已完成" + +#: app.py:728 +msgid "Invalid session" +msgstr "无效的会话" + +#: app.py:732 +msgid "Session is not active" +msgstr "会话未激活" + +#: app.py:780 +msgid "Invalid message" +msgstr "无效的消息" + #: templates/dashboard.html:58 msgid "No connections yet. Share your invite token to connect with others!" msgstr "还没有连接。分享您的邀请令牌以与他人连接!" From 672941a524886c49ff7b08b4017646f37d77d0cd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:29:54 +0000 Subject: [PATCH 4/5] Fix canvas size to match textarea and fix drawing trajectory playback - Changed canvas min-height from 300px to 400px to match textarea (400px) - Added mobile-specific canvas sizing (150px to match mobile textarea) - Fixed renderDrawing() to maintain path continuity - removed ctx.beginPath() and ctx.moveTo() after each draw point - Now receiver sees complete writing trajectory instead of moving dots Co-authored-by: w1010tdev <246258262+w1010tdev@users.noreply.github.com> --- static/css/style.css | 24 ++++++++++++++++++++---- templates/clipboard.html | 12 ++++++------ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/static/css/style.css b/static/css/style.css index ef8aada..aa6b431 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -450,7 +450,7 @@ body { background: #ffffff; padding: 0.5rem; flex: 1; - min-height: 300px; + min-height: 400px; } .clipboard-drawing-wrap canvas { @@ -475,11 +475,11 @@ body { } .clipboard-display.drawing-active { - min-height: 360px; + min-height: 400px; } .clipboard-pane.other-pane .clipboard-display.drawing-active { - min-height: 320px; + min-height: 400px; } .clipboard-container.handwriting-mode { @@ -495,7 +495,7 @@ body { } .clipboard-container.handwriting-mode .clipboard-pane.other-pane .clipboard-display.drawing-active { - min-height: 180px; + min-height: 300px; } .clipboard-textarea { @@ -1088,6 +1088,22 @@ body { font-size: 0.9rem; } + .clipboard-drawing-wrap { + min-height: 150px; + } + + .clipboard-display.drawing-active { + min-height: 150px; + } + + .clipboard-pane.other-pane .clipboard-display.drawing-active { + min-height: 150px; + } + + .clipboard-container.handwriting-mode .clipboard-pane.other-pane .clipboard-display.drawing-active { + min-height: 120px; + } + .clipboard-toolbar { flex-direction: column; align-items: flex-start; diff --git a/templates/clipboard.html b/templates/clipboard.html index 40656e2..11e995e 100644 --- a/templates/clipboard.html +++ b/templates/clipboard.html @@ -586,19 +586,19 @@

{{ _('Your Area (%(username)s)', username=curren } return; } - if (drawing.type === 'stroke_end' || drawing.type === 'stroke_start') { + if (drawing.type === 'stroke_start') { ctx.beginPath(); - if (drawing.type === 'stroke_start') { - ctx.moveTo(drawing.x, drawing.y); - } + ctx.moveTo(drawing.x, drawing.y); + return; + } + if (drawing.type === 'stroke_end') { return; } + // For 'draw' type - continue the current path ctx.lineWidth = 4; ctx.strokeStyle = '#1f2937'; ctx.lineTo(drawing.x, drawing.y); ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(drawing.x, drawing.y); if (ctx === otherDrawingCtx) { otherDrawingHasContent = true; } From 5b4117f198f68648904e024d5529ffda77598207 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 13:50:44 +0000 Subject: [PATCH 5/5] Refactor drawing rendering to match drawing game logic - Removed stroke_start emission - drawer now only emits 'draw' and 'stroke_end' events - Updated drawStroke() to match drawing game pattern: beginPath() and moveTo() after each point - Updated renderDrawing() to match drawing game receiver pattern exactly - Both drawer and receiver now use consistent ctx.beginPath()/moveTo() pattern - Added lineCap and lineJoin for smoother strokes - This ensures continuous stroke rendering on receiver side Co-authored-by: w1010tdev <246258262+w1010tdev@users.noreply.github.com> --- templates/clipboard.html | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/templates/clipboard.html b/templates/clipboard.html index 11e995e..f82e055 100644 --- a/templates/clipboard.html +++ b/templates/clipboard.html @@ -417,19 +417,18 @@

{{ _('Your Area (%(username)s)', username=curren lastY = e.clientY - rect.top; myDrawingCtx.beginPath(); myDrawingCtx.moveTo(lastX, lastY); - emitDrawing({ type: 'stroke_start', x: lastX, y: lastY }); } function drawStroke(x, y) { myDrawingCtx.lineWidth = 4; + myDrawingCtx.lineCap = 'round'; + myDrawingCtx.lineJoin = 'round'; myDrawingCtx.strokeStyle = '#1f2937'; myDrawingCtx.lineTo(x, y); myDrawingCtx.stroke(); myDrawingCtx.beginPath(); myDrawingCtx.moveTo(x, y); localDrawingHasContent = true; - lastX = x; - lastY = y; emitDrawing({ type: 'draw', x, y }); } @@ -438,8 +437,6 @@

{{ _('Your Area (%(username)s)', username=curren const rect = myDrawingCanvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; - myDrawingCtx.beginPath(); - myDrawingCtx.moveTo(lastX, lastY); drawStroke(x, y); lastX = x; lastY = y; @@ -487,7 +484,6 @@

{{ _('Your Area (%(username)s)', username=curren lastY = touch.clientY - rect.top; myDrawingCtx.beginPath(); myDrawingCtx.moveTo(lastX, lastY); - emitDrawing({ type: 'stroke_start', x: lastX, y: lastY }); }, { passive: false }); myDrawingCanvas.addEventListener('touchmove', (event) => { @@ -497,9 +493,9 @@

{{ _('Your Area (%(username)s)', username=curren const rect = myDrawingCanvas.getBoundingClientRect(); const x = touch.clientX - rect.left; const y = touch.clientY - rect.top; - myDrawingCtx.beginPath(); - myDrawingCtx.moveTo(lastX, lastY); drawStroke(x, y); + lastX = x; + lastY = y; }, { passive: false }); myDrawingCanvas.addEventListener('touchend', (event) => { @@ -521,7 +517,7 @@

{{ _('Your Area (%(username)s)', username=curren drawingPoints.push(drawing); } otherLastStroke = Date.now(); - if (drawing.type === 'stroke_start' || drawing.type === 'draw') { + if (drawing.type === 'draw') { setRemoteDrawingActive(true); } lastRemoteTimestamp = drawing.ts || lastRemoteTimestamp; @@ -586,21 +582,22 @@

{{ _('Your Area (%(username)s)', username=curren } return; } - if (drawing.type === 'stroke_start') { - ctx.beginPath(); - ctx.moveTo(drawing.x, drawing.y); - return; - } if (drawing.type === 'stroke_end') { + ctx.beginPath(); return; } - // For 'draw' type - continue the current path - ctx.lineWidth = 4; - ctx.strokeStyle = '#1f2937'; - ctx.lineTo(drawing.x, drawing.y); - ctx.stroke(); - if (ctx === otherDrawingCtx) { - otherDrawingHasContent = true; + if (drawing.type === 'draw') { + ctx.lineWidth = 4; + ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; + ctx.strokeStyle = '#1f2937'; + ctx.lineTo(drawing.x, drawing.y); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(drawing.x, drawing.y); + if (ctx === otherDrawingCtx) { + otherDrawingHasContent = true; + } } }