mirukana / mirage Goto Github PK
View Code? Open in Web Editor NEWA fancy, customizable, keyboard-operable Qt/QML & Python Matrix chat client for encrypted and decentralized communication.
License: GNU Lesser General Public License v3.0
A fancy, customizable, keyboard-operable Qt/QML & Python Matrix chat client for encrypted and decentralized communication.
License: GNU Lesser General Public License v3.0
It would be really nice to have proxy for mirage. This is a really helpfull feature for companys with a homeserver thats not publicly available and also very nice for privacy for example to use TOR.
It is nice to see kinetic scrolling.
On my setup:
I get the following behavior:
without any input the friction seems to work fine.
With input i get really bad results. the first input/velocity is picked up. But after that i jumps into kinetic mode and variations in speed almost take no effect. A change in direction though does.
of course this also could be a QT issue.
I have a mouse with a freely rotating wheel for quick scrolling. When looking through a long history I like to set the wheel spinning and then instantly stop when I see the thing I want. However Mirage's kinetic scrolling can easily scroll several screenfuls past the point where I wanted to stop. It's possible to effect an instant stop by pressing a mouse button, but this is an extra action I have to perform with my fingers compared to every other program. Given that the freely rotating wheel already does the inertia part on the physical device, I think a setting to disable kinetic scrolling would be appropriate. Maybe even make it the default when using a mouse, because users of traditional clicky wheels probably don't expect it either.
Separate chat rooms into direct messages, regular rooms (name just rooms), and low priority rooms. Possible designs are as in Riot or using tabs as in room details tab (the one on the right) of Mirage
I just noticed that literally all the voice messages coming from both the Telegram and WhatsApp bridge are not properly visible.
They only show up as being downloadable, although they could easily just be played instead, with the missing information being fetch-able as well.
I can also not copy off the dev-console directly.
I exported my decryption keys using the riot web interface, but get the following error when trying to import them:
Traceback (most recent call last):
File "qrc:/src/backend/qml_bridge.py", line 82, in on_done
result = future.result()
File "/usr/lib/python3.8/concurrent/futures/_base.py", line 432, in result
return self.__get_result()
File "/usr/lib/python3.8/concurrent/futures/_base.py", line 388, in __get_result
raise self._exception
File "qrc:/src/backend/matrix_client.py", line 1151, in import_keys
await self.retry_decrypting_events()
File "qrc:/src/backend/matrix_client.py", line 1179, in retry_decrypting_events
for ev in deepcopy(model).values():
File "/usr/lib/python3.8/copy.py", line 172, in deepcopy
y = _reconstruct(x, memo, *rv)
File "/usr/lib/python3.8/copy.py", line 270, in _reconstruct
state = deepcopy(state, memo)
File "/usr/lib/python3.8/copy.py", line 146, in deepcopy
y = copier(x, memo)
File "/usr/lib/python3.8/copy.py", line 230, in _deepcopy_dict
y[deepcopy(key, memo)] = deepcopy(value, memo)
File "/usr/lib/python3.8/copy.py", line 161, in deepcopy
rv = reductor(4)
TypeError: cannot pickle '_thread.RLock' object
Fresh install on Arch Linux, python 3.8.3, mirage 0.5.0
I've been leaving Riot open in a web window to get notifications even though I'm not using it. I see that Notifications are in the TODO file. Can you expand a bit on your expectations? I might be able to lend a hand with it.
When addressing other user, would be great to have autocomplete of the user's names. For example, via line presented on the top of the written message in message pane. As in
message 1
message 2
username1 username2 username3 ...
> usernam_
It's pretty annoying when it causes the entire chat timeline to move up and down when the notification appears and disappears.
It looks like messages have a max width of about 585 pixels (37.5 times the font size, looking at the source). This means that if I maximize the Mirage window on my 1440p screen, only about one third of the width available for messages is utilized. I know it gets difficult to follow lines if they are too long, but I think on desktop systems users should be able to decide what they're comfortable with by adjusting the window width.
Greetings.
Is this possible to support SAML/SSO?
https://matrix.org/docs/spec/client_server/r0.5.0#sso-client-login
When trying to import the E2E room keys, I get this error after a few seconds:
Traceback (most recent call last):
File "src/backend/qml_bridge.py", line 78, in on_done
result = future.result()
File "/usr/lib/python3.8/concurrent/futures/_base.py", line 432, in result
return self.__get_result()
File "/usr/lib/python3.8/concurrent/futures/_base.py", line 388, in __get_result
raise self._exception
File "src/backend/matrix_client.py", line 1051, in import_keys
await self.retry_decrypting_events()
File "src/backend/matrix_client.py", line 1094, in retry_decrypting_events
await asyncio.coroutine(cb.func)(room, decrypted)
File "src/backend/nio_callbacks.py", line 108, in onRoomMessageText
await self.client.register_nio_event(
File "src/backend/matrix_client.py", line 1311, in register_nio_event
model[item.id] = item
File "src/backend/models/model.py", line 82, in __setitem__
setattr(existing, field, getattr(new, field))
File "src/backend/models/model_item.py", line 51, in __setattr__
self.serialize_field(name),
File "src/backend/models/items.py", line 227, in serialize_field
source_dict = nio.attr.asdict(self.source) if self.source else {}
AttributeError: module 'nio' has no attribute 'attr'
I get this error (or very similar, file paths might be slightly different) on both the Arch Linux AUR installation of Mirage and a manual installation.
The problem seems to be caused by newer versions of the matrix-nio package. Installing a slightly older version, v0.10.0, from the upstream repo like so
pip install "git+git://github.com/poljar/[email protected]#egg=matrix-nio[e2e]"
seems to fix the issue.
I can't seem to start SAS verification (either from Mirage or to Mirage), or view device keys. Am I missing something?
It'd be really nice to have an AUR package for this, if anyone has expertise in that.
This might be the most beautiful Matrix client out there - simply for it's customizeability alone. Therefore I was very intrigued by the screenshots alone but my excitement came to a hard stop when I realized that it was only available on Linux.
What exactly is stopping Mirage from being available on other platforms such as macOS and Windows?
I am in a lot of groups but also use Matrix as a do-it-all messenger for WhatsApp, Facebook and Telegram. In Riot, I often take advantage of classifying something as a "direct chat", while pinning a few chats as my absolute favorites (gotta find my mom fast to reply, or she'll be mad... :P )
I am a bit overrun right now with just how many things popped up in the sidebar and i have to find some way of organizing that now. Oof.
Running mirage
right after installing it on my Archlinux setup causes an infinite spinner and prints the following in the following to be printed in the terminal:
Got library name: "/usr/lib/qt/qml/io/thp/pyotherside/libpyothersideplugin.so"
"PyOtherSide error: Traceback (most recent call last):\n\n File \"src/backend/qml_bridge.py\", line 148, in <module>\n BRIDGE = QMLBridge()\n\n File \"src/backend/qml_bridge.py\", line 43, in __init__\n from .backend import Backend\n\n File \"src/backend/backend.py\", line 15, in <module>\n from .matrix_client import MatrixClient\n\n File \"src/backend/matrix_client.py\", line 39, in <module>\n from .media_cache import Media, Thumbnail\n\n File \"src/backend/media_cache.py\", line 30, in <module>\n CONCURRENT_DOWNLOADS_LIMIT = asyncio.BoundedSemaphore(8)\n\n File \"/usr/lib/python3.8/asyncio/locks.py\", line 529, in __init__\n super().__init__(value, loop=loop)\n\n File \"/usr/lib/python3.8/asyncio/locks.py\", line 458, in __init__\n self._loop = events.get_event_loop()\n\n File \"/usr/lib/python3.8/asyncio/events.py\", line 639, in get_event_loop\n raise RuntimeError('There is no current event loop in thread %r.'\n\nRuntimeError: There is no current event loop in thread 'Dummy-1'.\n"
Unhandled PyOtherSide error: Cannot import module: backend.qml_bridge (Traceback (most recent call last):
File "src/backend/qml_bridge.py", line 148, in <module>
BRIDGE = QMLBridge()
File "src/backend/qml_bridge.py", line 43, in __init__
from .backend import Backend
File "src/backend/backend.py", line 15, in <module>
from .matrix_client import MatrixClient
File "src/backend/matrix_client.py", line 39, in <module>
from .media_cache import Media, Thumbnail
File "src/backend/media_cache.py", line 30, in <module>
CONCURRENT_DOWNLOADS_LIMIT = asyncio.BoundedSemaphore(8)
File "/usr/lib/python3.8/asyncio/locks.py", line 529, in __init__
super().__init__(value, loop=loop)
File "/usr/lib/python3.8/asyncio/locks.py", line 458, in __init__
self._loop = events.get_event_loop()
File "/usr/lib/python3.8/asyncio/events.py", line 639, in get_event_loop
raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Dummy-1'.
)
"PyOtherSide error: Traceback (most recent call last):\n\n File \"<string>\", line 1, in <module>\n\nNameError: name 'BRIDGE' is not defined\n"
Unhandled PyOtherSide error: Function not found: 'BRIDGE.call_backend_coro' (Traceback (most recent call last):
File "<string>", line 1, in <module>
NameError: name 'BRIDGE' is not defined
)
If I whip the scroll wheel on the room list to make it scroll really fast, it crashes it 100% of the time for me.
yay -S matrix-mirage
Scroll through the list really fast, stop at the bottom.
Segmentation fault (core dumped)
(let me know if there's a way I can get a more detailed error)
I installed both the flatpak package and the Appimage on Arch Linux. When I use flatpak it crashes after I attemp to log in to my account. It works fine with the AppImage
$ flatpak run io.github.mirukana.mirage
Got library name: "/app/usr/lib/qml/io/thp/pyotherside/libpyothersideplugin.so"
This is everything shown in stdout. I'd be happy to supply more error messages. Is there a debug mode?
After opening the program, it gets stuck for a long time (10+ minutes) loading rooms and leaks memory while doing so. Tried on both most recent Flatpak and AppImage.
I have rather odd crash in the last version of Mirage on Sailfish, using Flatpak for ARM 32 bit; Wayland.
Its hard to debug, but bisect pointed to ad93757 as a commit introducing it. Doesn't really help that this is a large one.
Crash is after showing busy indicator and, for a moment, the background.
Backtrace:
Thread 1 "mirage" received signal SIGSEGV, Segmentation fault.
0xf69c30a0 in QMetaObject::propertyOffset() const () from /usr/lib/arm-linux-gnueabihf/libQt5Core.so.5
(gdb) bt
#0 0xf69c30a0 in QMetaObject::propertyOffset() const () from /usr/lib/arm-linux-gnueabihf/libQt5Core.so.5
#1 0xf69c400a in QMetaObject::property(int) const () from /usr/lib/arm-linux-gnueabihf/libQt5Core.so.5
#2 0xf6e1de2e in QQmlBinding::getPropertyData(QQmlPropertyData**, QQmlPropertyData*) const ()
from /usr/lib/arm-linux-gnueabihf/libQt5Qml.so.5
#3 0xf6e1e934 in ?? () from /usr/lib/arm-linux-gnueabihf/libQt5Qml.so.5
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
When clicking on image, maximize it instead of opening in the external application (browser).
That would require some way of navigatation back. As an example, drawer can be used (from bottom or some other side) allowing users to swipe it out. In addition, X in the corner could be used as well.
Issue: When using the current instructions to install via Flatpak, flatpak install --user flathub org.kde.Platform//5.12 mirage-*.flatpak
Get the following:
Looking for matches…
error: No remote refs found similar to ‘flathub’
Solution: Update the cited reference to point to correct source
Other: Found this error using Manjaro OS
When sending a message in an end-to-end encrypted room, getting the following error message every time. Do I need to do something to force a sync? Will this eventually sort out?:
Traceback (most recent call last):
File "qrc:/src/backend/qml_bridge.py", line 82, in on_done
result = future.result()
File "/usr/lib/python3.8/concurrent/futures/_base.py", line 432, in result
return self.__get_result()
File "/usr/lib/python3.8/concurrent/futures/_base.py", line 388, in __get_result
raise self._exception
File "qrc:/src/backend/matrix_client.py", line 454, in send_text
await self._send_message(room_id, content, tx_id)
File "qrc:/src/backend/matrix_client.py", line 769, in _send_message
await self.room_send(
File "/usr/lib/python3.8/site-packages/nio/client/async_client.py", line 1480, in room_send
message_type, content = self.encrypt(
File "/usr/lib/python3.8/site-packages/nio/client/base_client.py", line 116, in inner
return fn(self, *args, **kwargs)
File "/usr/lib/python3.8/site-packages/nio/client/base_client.py", line 1159, in encrypt
raise MembersSyncError(
nio.exceptions.MembersSyncError: The room is encrypted and the members aren't fully synced.
yay -S matrix-mirage
When the window is made narrow, side panels are collapsed to icons (before disappearing). I am not sure whether this mode is that useful. However, if someone likes it, then it should also be possible to make left and right panel full size, by making it the main one. Currently its possible only when the window is too narrow to show icons by moving main pane left/right.
I just built on macOS - INSTALL.md pretty much worked without needing any changes :)
However, i can't enter # in the app (e.g. to join a room) because # on macOS on a UK keyboard is alt+3 - which instead switches to the third tab in the room list. It'd probably be more idiomatic on macOS to use cmd-+3 for this (e.g. switching tabs in Chrome is cmd+number).
When using Mirage in a narrow mode (only one pane visible, as on mobile), the right pane is frequently shown as an overlay on the top of the main pane.
To reproduce:
As a result, the right pane will be rendered on top of the main one. If you resize the window to wide mode and back to the narrow one, all will work as it should.
When there are unread messages in a room, highlight it somehow.
I noticed that when scrolling upwards through a long channel history, Mirage crashes pretty quickly. There's no messages in the terminal but gdb reveals it to be a SIGPIPE:
Thread 7 "QPythonWorker" received signal SIGPIPE, Broken pipe.
[Switching to Thread 0x7fffd562a700 (LWP 13043)]
__GI___libc_write (nbytes=24, buf=0x7fffddec9c10, fd=41) at ../sysdeps/unix/sysv/linux/write.c:26
26 ../sysdeps/unix/sysv/linux/write.c: No such file or directory.
(gdb) bt
#0 __GI___libc_write (nbytes=24, buf=0x7fffddec9c10, fd=41) at ../sysdeps/unix/sysv/linux/write.c:26
#1 __GI___libc_write (fd=fd@entry=41, buf=buf@entry=0x7fffddec9c10, nbytes=nbytes@entry=24)
at ../sysdeps/unix/sysv/linux/write.c:24
#2 0x00007fffd6c10620 in __pyx_f_6uvloop_4loop_8UVStream__try_write (__pyx_v_self=0x7fffcc0bb860, __pyx_v_data=<optimized out>)
at uvloop/loop.c:87771
#3 0x00007fffd6c21d62 in __pyx_f_6uvloop_4loop_8UVStream__exec_write (__pyx_v_self=__pyx_v_self@entry=0x7fffcc0bb860)
at uvloop/loop.c:88613
#4 0x00007fffd6c24e08 in __pyx_f_6uvloop_4loop_4Loop__exec_queued_writes (__pyx_v_self=0x7fffe0625820) at uvloop/loop.c:15829
#5 0x00007fffd6c50360 in __pyx_f_6uvloop_4loop_6Handle__run (__pyx_v_self=__pyx_v_self@entry=0x7fffd6f02670)
at uvloop/loop.c:56458
#6 0x00007fffd6c53374 in __pyx_f_6uvloop_4loop_cb_check_callback (__pyx_v_handle=<optimized out>) at uvloop/loop.c:76086
#7 0x00007fffd6c62d61 in uv__run_check (loop=loop@entry=0x7fffe0234e40) at src/unix/loop-watcher.c:67
#8 0x00007fffd6c606dd in uv_run (loop=0x7fffe0234e40, mode=mode@entry=UV_RUN_DEFAULT) at src/unix/core.c:376
#9 0x00007fffd6c1c100 in __pyx_f_6uvloop_4loop_4Loop___run (__pyx_v_self=0x7fffe0625820, __pyx_v_mode=UV_RUN_DEFAULT)
at uvloop/loop.c:13914
#10 0x00007fffd6c254c2 in __pyx_f_6uvloop_4loop_4Loop__run (__pyx_v_self=0x7fffe0625820, __pyx_v_mode=UV_RUN_DEFAULT)
at uvloop/loop.c:14299
#11 0x00007fffd6b8c315 in __pyx_pf_6uvloop_4loop_4Loop_24run_forever (__pyx_v_self=0x7fffe0625820) at uvloop/loop.c:27024
#12 __pyx_pw_6uvloop_4loop_4Loop_25run_forever (__pyx_v_self=0x7fffe0625820, unused=<optimized out>) at uvloop/loop.c:26827
#13 0x00007fffebc79297 in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#14 0x00007fffeba51ffd in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#15 0x00007fffeba53274 in _PyEval_EvalFrameDefault () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#16 0x00007fffeba5d0eb in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#17 0x00007fffebc80bab in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#18 0x00007fffebc805cc in PyVectorcall_Call () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#19 0x00007fffeba57b29 in _PyEval_EvalFrameDefault () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#20 0x00007fffeba5d0eb in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#21 0x00007fffeba51ffd in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#22 0x00007fffeba53274 in _PyEval_EvalFrameDefault () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#23 0x00007fffeba5d0eb in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#24 0x00007fffeba51ffd in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#25 0x00007fffeba53274 in _PyEval_EvalFrameDefault () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#26 0x00007fffeba5d0eb in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#27 0x00007fffebc80bab in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#28 0x00007fffebc805cc in PyVectorcall_Call () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#29 0x00007fffebaf23dd in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#30 0x00007fffebb57d67 in ?? () from /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0
#31 0x00007ffff5c16f27 in start_thread (arg=<optimized out>) at pthread_create.c:479
#32 0x00007ffff60442ef in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
I'm not sure if this is a bug in uvloop, the nio library or Mirage itself. Disabling uvloop fixes it, as does adding signal(SIGPIPE, SIG_IGN);
at the top of main().
I have a account with about 100 rooms. (which is definitly not too much)
And the startup time is really long. It definitly loads longer than one minute.
Ability to add reactions to a message. Emoji should be the minimum, but since the API allows for any text to be a reaction, allowing the user to type any text for a reaction as well would be a nice touch.
I use a 13" MacBook from 2010, so the resolution isn't exactly great. And the side bar unfortunately takes away a lot of valuable space there.
Would it be possible to make the side-bar hide-able? Thanks!
If Mirage 0.5.0 is launched with a single account, something is written in the room filter, and then Mirage is closed and launched again, a ghost account seems to be created, which is stuck in a continuous loading state. Clearing the filter and restarting Mirage fixes the problem.
This issue was not present in the 0.4.3 release. I don't have more than one account to test what happens if more than one is logged in.
Below is a screenshot of the issue, where the account in the loading state is a duplicate of the first account (apologies if the "sanitation" impedes understanding of the image; I speak with some people that would take issue with any names or messages being shared publicly).
When a message is edited, the edit is applied to the original message, but the edit event also appears as a message on its own. This can result in some confusing chat history sometimes. Mirage does not currently offer an editing UI so testing requires using another client or finding a room where edits have happened.
Would be great to have it packaged into Flatpak as well. There are two parts of it:
I will look into writing packaging script, as it would allow me to start using it on mobile.
please do not make install
under /usr
by default.
I tried to upload a profile picture and got a 500 response from the server. Synapse's log complains about missing Content-Length header. This is arguably a bug with Synapse, but given that it was reported there a year ago and still hasn't been fixed, would it be possible to make Mirage send uploads using content length instead of chunked transfer encoding?
In GNOME, I am getting constantly notifications with "Mirage ready". Would be great to get rid of it.
When testing on mobile, top header and text field on the bottom are somewhat small. While text size is fine as it is, it is hard to select the text field (or let's say harder than it should be). So, I suggest to make header and footer of the pages the same as the height of a single room delegate in list room pane. That size feels spot on.
The icons on buttons, in the header/footer, are fine in terms of the size. It's just that they would have to be centered vertically in the corresponding header/footer and the area allowing to select them should be made a bigger.
Riot Web provides this feature. I'm not sure if this is as simple as never sending receipts if enabled.
When user of Mirage is addressed in the message, add highlights
It would be nice to have a space to share themes. What is the approach for that? Can i find more themes somewhere in the internet?
After fixing the Python error in #15 I can start Mirage but then it starts spewing QML errors:
qrc:/src/gui/MainPane/Room.qml:110: TypeError: Cannot call method 'startsWith' of undefined
qrc:/src/gui/MainPane/Room.qml:110: TypeError: Cannot call method 'startsWith' of undefined
qrc:/src/gui/MainPane/Room.qml:110: TypeError: Type error
qrc:/src/gui/MainPane/Room.qml:110: TypeError: Type error
qrc:/src/gui/MainPane/Room.qml:110: TypeError: Type error
qrc:/src/gui/MainPane/Room.qml:110: TypeError: Type error
qrc:/src/gui/MainPane/Room.qml:110: TypeError: Type error
qrc:/src/gui/MainPane/Room.qml:110: TypeError: Type error
qrc:/src/gui/MainPane/Room.qml:110: TypeError: Type error
qrc:/src/gui/Pages/Chat/Timeline/Daybreak.qml:7: TypeError: Type error
qrc:/src/gui/Pages/Chat/Timeline/EventContent.qml:90:13: Unable to assign [undefined] to QString
qrc:/src/gui/Pages/Chat/Timeline/EventContent.qml:89:13: Unable to assign [undefined] to QString
qrc:/src/gui/Pages/Chat/Timeline/EventContent.qml:91:13: Unable to assign [undefined] to QString
qrc:/src/gui/Utils.qml:167: TypeError: Type error
qrc:/src/gui/Pages/Chat/Timeline/EventDelegate.qml:39: TypeError: Type error
qrc:/src/gui/Pages/Chat/Timeline/EventContent.qml: TypeError: Type error
qrc:/src/gui/Pages/Chat/Timeline/EventContent.qml:14: SyntaxError: JSON.parse: Parse error
qrc:/src/gui/Utils.qml:167: TypeError: Type error
qrc:/src/gui/Pages/Chat/Timeline/EventContent.qml:266: TypeError: Type error
qrc:/src/gui/Pages/Chat/Timeline/EventContent.qml:274: SyntaxError: JSON.parse: Parse error
qrc:/src/gui/Pages/Chat/Timeline/Daybreak.qml:7: TypeError: Type error
qrc:/src/gui/Pages/Chat/Timeline/EventContent.qml:90:13: Unable to assign [undefined] to QString
qrc:/src/gui/Pages/Chat/Timeline/EventContent.qml:89:13: Unable to assign [undefined] to QString
All messages in the room are displayed with a question mark avatar and empty text.
I can investigate this further when I have time, but if you have any insights that would be appreciated.
When logged in to an account the following warning shows up on stdout.
WARNING:root:On @besendorf:matrix.org client startup: MatrixError(http_code=401, m_code='M_MISSING_TOKEN')
View reactions on a message, regardless of whether they are emoji, or text.
UI to search rooms on homeservers like Riot.
Similarily, have a UI when creating rooms for publishing in room directory and setting addresses.
Currently, Mirage is not marking the messages as read in the room history in a way that other clients would understand that as well. Please implement such indicator to make it easier to mix the clients.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.