ommlds 0.0.0.dev480__py3-none-any.whl → 0.0.0.dev503__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ommlds/.omlish-manifests.json +100 -33
- ommlds/README.md +11 -0
- ommlds/__about__.py +9 -6
- ommlds/backends/anthropic/protocol/__init__.py +13 -1
- ommlds/backends/anthropic/protocol/_dataclasses.py +1625 -0
- ommlds/backends/anthropic/protocol/sse/events.py +2 -0
- ommlds/backends/cerebras/__init__.py +7 -0
- ommlds/backends/cerebras/_dataclasses.py +4254 -0
- ommlds/backends/cerebras/_marshal.py +24 -0
- ommlds/backends/cerebras/protocol.py +312 -0
- ommlds/backends/google/protocol/__init__.py +13 -0
- ommlds/backends/google/protocol/_dataclasses.py +5997 -0
- ommlds/backends/groq/__init__.py +7 -0
- ommlds/backends/groq/_dataclasses.py +3901 -0
- ommlds/backends/groq/clients.py +9 -0
- ommlds/backends/llamacpp/logging.py +4 -1
- ommlds/backends/mlx/caching.py +7 -3
- ommlds/backends/mlx/cli.py +10 -7
- ommlds/backends/mlx/generation.py +18 -16
- ommlds/backends/mlx/limits.py +10 -6
- ommlds/backends/mlx/loading.py +7 -4
- ommlds/backends/ollama/__init__.py +7 -0
- ommlds/backends/ollama/_dataclasses.py +3488 -0
- ommlds/backends/ollama/protocol.py +3 -0
- ommlds/backends/openai/protocol/__init__.py +15 -1
- ommlds/backends/openai/protocol/_dataclasses.py +7708 -0
- ommlds/backends/tavily/__init__.py +7 -0
- ommlds/backends/tavily/_dataclasses.py +1734 -0
- ommlds/backends/transformers/__init__.py +14 -0
- ommlds/cli/__init__.py +7 -0
- ommlds/cli/_dataclasses.py +3515 -0
- ommlds/cli/backends/catalog.py +0 -5
- ommlds/cli/backends/inject.py +70 -7
- ommlds/cli/backends/meta.py +82 -0
- ommlds/cli/content/messages.py +1 -1
- ommlds/cli/inject.py +11 -3
- ommlds/cli/main.py +137 -68
- ommlds/cli/rendering/types.py +6 -0
- ommlds/cli/secrets.py +2 -1
- ommlds/cli/sessions/base.py +1 -10
- ommlds/cli/sessions/chat/configs.py +9 -17
- ommlds/cli/sessions/chat/{chat → drivers}/ai/configs.py +3 -1
- ommlds/cli/sessions/chat/drivers/ai/events.py +57 -0
- ommlds/cli/sessions/chat/{chat → drivers}/ai/inject.py +10 -3
- ommlds/cli/sessions/chat/{chat → drivers}/ai/rendering.py +1 -1
- ommlds/cli/sessions/chat/{chat → drivers}/ai/services.py +1 -1
- ommlds/cli/sessions/chat/{chat → drivers}/ai/tools.py +4 -8
- ommlds/cli/sessions/chat/{chat → drivers}/ai/types.py +9 -0
- ommlds/cli/sessions/chat/drivers/configs.py +25 -0
- ommlds/cli/sessions/chat/drivers/events/inject.py +27 -0
- ommlds/cli/sessions/chat/drivers/events/injection.py +14 -0
- ommlds/cli/sessions/chat/drivers/events/manager.py +16 -0
- ommlds/cli/sessions/chat/drivers/events/types.py +38 -0
- ommlds/cli/sessions/chat/drivers/impl.py +50 -0
- ommlds/cli/sessions/chat/drivers/inject.py +70 -0
- ommlds/cli/sessions/chat/{chat → drivers}/state/configs.py +2 -0
- ommlds/cli/sessions/chat/drivers/state/ids.py +25 -0
- ommlds/cli/sessions/chat/drivers/state/inject.py +83 -0
- ommlds/cli/sessions/chat/{chat → drivers}/state/inmemory.py +0 -4
- ommlds/cli/sessions/chat/{chat → drivers}/state/storage.py +17 -10
- ommlds/cli/sessions/chat/{chat → drivers}/state/types.py +10 -5
- ommlds/cli/sessions/chat/{tools → drivers/tools}/configs.py +2 -2
- ommlds/cli/sessions/chat/drivers/tools/confirmation.py +44 -0
- ommlds/cli/sessions/chat/drivers/tools/errorhandling.py +39 -0
- ommlds/cli/sessions/chat/{tools → drivers/tools}/execution.py +3 -4
- ommlds/cli/sessions/chat/{tools → drivers/tools}/fs/inject.py +3 -3
- ommlds/cli/sessions/chat/{tools → drivers/tools}/inject.py +7 -12
- ommlds/cli/sessions/chat/{tools → drivers/tools}/injection.py +5 -5
- ommlds/cli/sessions/chat/{tools → drivers/tools}/rendering.py +3 -3
- ommlds/cli/sessions/chat/{tools → drivers/tools}/todo/inject.py +3 -3
- ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/tools.py +1 -1
- ommlds/cli/sessions/chat/drivers/types.py +31 -0
- ommlds/cli/sessions/chat/{chat → drivers}/user/configs.py +0 -3
- ommlds/cli/sessions/chat/drivers/user/inject.py +41 -0
- ommlds/cli/sessions/chat/facades/__init__.py +0 -0
- ommlds/cli/sessions/chat/facades/commands/__init__.py +0 -0
- ommlds/cli/sessions/chat/facades/commands/base.py +83 -0
- ommlds/cli/sessions/chat/facades/commands/configs.py +9 -0
- ommlds/cli/sessions/chat/facades/commands/inject.py +41 -0
- ommlds/cli/sessions/chat/facades/commands/injection.py +15 -0
- ommlds/cli/sessions/chat/facades/commands/manager.py +59 -0
- ommlds/cli/sessions/chat/facades/commands/simple.py +34 -0
- ommlds/cli/sessions/chat/facades/commands/types.py +13 -0
- ommlds/cli/sessions/chat/facades/configs.py +11 -0
- ommlds/cli/sessions/chat/facades/facade.py +26 -0
- ommlds/cli/sessions/chat/facades/inject.py +35 -0
- ommlds/cli/sessions/chat/facades/ui.py +34 -0
- ommlds/cli/sessions/chat/inject.py +8 -31
- ommlds/cli/sessions/chat/interfaces/__init__.py +0 -0
- ommlds/cli/sessions/chat/interfaces/bare/__init__.py +0 -0
- ommlds/cli/sessions/chat/interfaces/bare/configs.py +15 -0
- ommlds/cli/sessions/chat/interfaces/bare/inject.py +69 -0
- ommlds/cli/sessions/chat/interfaces/bare/interactive.py +49 -0
- ommlds/cli/sessions/chat/interfaces/bare/oneshot.py +21 -0
- ommlds/cli/sessions/chat/{tools/confirmation.py → interfaces/bare/tools.py} +3 -22
- ommlds/cli/sessions/chat/interfaces/base.py +13 -0
- ommlds/cli/sessions/chat/interfaces/configs.py +11 -0
- ommlds/cli/sessions/chat/interfaces/inject.py +29 -0
- ommlds/cli/sessions/chat/interfaces/textual/__init__.py +0 -0
- ommlds/cli/sessions/chat/interfaces/textual/app.py +310 -0
- ommlds/cli/sessions/chat/interfaces/textual/configs.py +11 -0
- ommlds/cli/sessions/chat/interfaces/textual/facades.py +19 -0
- ommlds/cli/sessions/chat/interfaces/textual/inject.py +97 -0
- ommlds/cli/sessions/chat/interfaces/textual/interface.py +24 -0
- ommlds/cli/sessions/chat/interfaces/textual/styles/__init__.py +29 -0
- ommlds/cli/sessions/chat/interfaces/textual/styles/input.tcss +53 -0
- ommlds/cli/sessions/chat/interfaces/textual/styles/markdown.tcss +7 -0
- ommlds/cli/sessions/chat/interfaces/textual/styles/messages.tcss +157 -0
- ommlds/cli/sessions/chat/interfaces/textual/tools.py +38 -0
- ommlds/cli/sessions/chat/interfaces/textual/widgets/__init__.py +0 -0
- ommlds/cli/sessions/chat/interfaces/textual/widgets/input.py +36 -0
- ommlds/cli/sessions/chat/interfaces/textual/widgets/messages.py +197 -0
- ommlds/cli/sessions/chat/session.py +8 -13
- ommlds/cli/sessions/completion/configs.py +3 -4
- ommlds/cli/sessions/completion/inject.py +1 -2
- ommlds/cli/sessions/completion/session.py +4 -8
- ommlds/cli/sessions/configs.py +10 -0
- ommlds/cli/sessions/embedding/configs.py +3 -4
- ommlds/cli/sessions/embedding/inject.py +1 -2
- ommlds/cli/sessions/embedding/session.py +4 -8
- ommlds/cli/sessions/inject.py +15 -15
- ommlds/cli/state/storage.py +7 -1
- ommlds/minichain/__init__.py +161 -38
- ommlds/minichain/_dataclasses.py +20452 -0
- ommlds/minichain/_typedvalues.py +11 -4
- ommlds/minichain/backends/impls/anthropic/names.py +3 -3
- ommlds/minichain/backends/impls/anthropic/protocol.py +2 -2
- ommlds/minichain/backends/impls/anthropic/stream.py +1 -1
- ommlds/minichain/backends/impls/cerebras/__init__.py +0 -0
- ommlds/minichain/backends/impls/cerebras/chat.py +80 -0
- ommlds/minichain/backends/impls/cerebras/names.py +45 -0
- ommlds/minichain/backends/impls/cerebras/protocol.py +143 -0
- ommlds/minichain/backends/impls/cerebras/stream.py +125 -0
- ommlds/minichain/backends/impls/duckduckgo/search.py +5 -1
- ommlds/minichain/backends/impls/google/names.py +6 -0
- ommlds/minichain/backends/impls/google/stream.py +1 -1
- ommlds/minichain/backends/impls/google/tools.py +2 -2
- ommlds/minichain/backends/impls/groq/chat.py +2 -0
- ommlds/minichain/backends/impls/groq/protocol.py +2 -2
- ommlds/minichain/backends/impls/groq/stream.py +3 -1
- ommlds/minichain/backends/impls/huggingface/repos.py +1 -5
- ommlds/minichain/backends/impls/llamacpp/chat.py +6 -3
- ommlds/minichain/backends/impls/llamacpp/completion.py +7 -3
- ommlds/minichain/backends/impls/llamacpp/stream.py +6 -3
- ommlds/minichain/backends/impls/mlx/chat.py +6 -3
- ommlds/minichain/backends/impls/ollama/chat.py +51 -57
- ommlds/minichain/backends/impls/ollama/protocol.py +144 -0
- ommlds/minichain/backends/impls/openai/format.py +4 -3
- ommlds/minichain/backends/impls/openai/names.py +3 -1
- ommlds/minichain/backends/impls/openai/stream.py +33 -1
- ommlds/minichain/backends/impls/sentencepiece/tokens.py +9 -6
- ommlds/minichain/backends/impls/tinygrad/chat.py +7 -4
- ommlds/minichain/backends/impls/tokenizers/tokens.py +9 -6
- ommlds/minichain/backends/impls/transformers/sentence.py +5 -2
- ommlds/minichain/backends/impls/transformers/tokens.py +9 -6
- ommlds/minichain/backends/impls/transformers/transformers.py +10 -8
- ommlds/minichain/backends/strings/resolving.py +1 -1
- ommlds/minichain/chat/content.py +42 -0
- ommlds/minichain/chat/messages.py +43 -39
- ommlds/minichain/chat/stream/joining.py +36 -12
- ommlds/minichain/chat/stream/types.py +1 -1
- ommlds/minichain/chat/templating.py +3 -3
- ommlds/minichain/content/__init__.py +19 -3
- ommlds/minichain/content/_marshal.py +181 -55
- ommlds/minichain/content/code.py +26 -0
- ommlds/minichain/content/composite.py +28 -0
- ommlds/minichain/content/content.py +27 -0
- ommlds/minichain/content/dynamic.py +12 -0
- ommlds/minichain/content/emphasis.py +27 -0
- ommlds/minichain/content/images.py +2 -2
- ommlds/minichain/content/json.py +2 -2
- ommlds/minichain/content/link.py +13 -0
- ommlds/minichain/content/markdown.py +12 -0
- ommlds/minichain/content/metadata.py +10 -0
- ommlds/minichain/content/namespaces.py +8 -0
- ommlds/minichain/content/placeholders.py +10 -9
- ommlds/minichain/content/quote.py +26 -0
- ommlds/minichain/content/raw.py +49 -0
- ommlds/minichain/content/recursive.py +12 -0
- ommlds/minichain/content/section.py +26 -0
- ommlds/minichain/content/sequence.py +17 -3
- ommlds/minichain/content/standard.py +32 -0
- ommlds/minichain/content/tag.py +28 -0
- ommlds/minichain/content/templates.py +13 -0
- ommlds/minichain/content/text.py +2 -2
- ommlds/minichain/content/transform/__init__.py +0 -0
- ommlds/minichain/content/transform/json.py +55 -0
- ommlds/minichain/content/transform/markdown.py +8 -0
- ommlds/minichain/content/transform/materialize.py +51 -0
- ommlds/minichain/content/transform/metadata.py +16 -0
- ommlds/minichain/content/{prepare.py → transform/prepare.py} +10 -15
- ommlds/minichain/content/transform/recursive.py +97 -0
- ommlds/minichain/content/transform/standard.py +43 -0
- ommlds/minichain/content/{transforms → transform}/stringify.py +1 -7
- ommlds/minichain/content/transform/strings.py +33 -0
- ommlds/minichain/content/transform/templates.py +25 -0
- ommlds/minichain/content/visitors.py +231 -0
- ommlds/minichain/lib/fs/tools/read.py +1 -1
- ommlds/minichain/lib/fs/tools/recursivels/rendering.py +1 -1
- ommlds/minichain/lib/fs/tools/recursivels/running.py +1 -1
- ommlds/minichain/lib/todo/tools/write.py +2 -1
- ommlds/minichain/lib/todo/types.py +1 -1
- ommlds/minichain/metadata.py +56 -2
- ommlds/minichain/resources.py +22 -1
- ommlds/minichain/services/README.md +154 -0
- ommlds/minichain/services/__init__.py +6 -2
- ommlds/minichain/services/_marshal.py +46 -10
- ommlds/minichain/services/_origclasses.py +11 -0
- ommlds/minichain/services/_typedvalues.py +8 -3
- ommlds/minichain/services/requests.py +73 -3
- ommlds/minichain/services/responses.py +73 -3
- ommlds/minichain/services/services.py +9 -0
- ommlds/minichain/stream/services.py +24 -1
- ommlds/minichain/text/applypatch.py +2 -1
- ommlds/minichain/text/toolparsing/llamacpp/types.py +1 -1
- ommlds/minichain/tokens/specials.py +1 -1
- ommlds/minichain/tools/execution/catalog.py +1 -1
- ommlds/minichain/tools/execution/errorhandling.py +36 -0
- ommlds/minichain/tools/execution/errors.py +2 -2
- ommlds/minichain/tools/execution/executors.py +1 -1
- ommlds/minichain/tools/fns.py +1 -1
- ommlds/minichain/tools/jsonschema.py +2 -2
- ommlds/minichain/tools/reflect.py +6 -6
- ommlds/minichain/tools/types.py +12 -15
- ommlds/minichain/vectors/_marshal.py +1 -1
- ommlds/minichain/vectors/embeddings.py +1 -1
- ommlds/minichain/wrappers/__init__.py +7 -0
- ommlds/minichain/wrappers/firstinwins.py +144 -0
- ommlds/minichain/wrappers/instrument.py +146 -0
- ommlds/minichain/wrappers/retry.py +168 -0
- ommlds/minichain/wrappers/services.py +98 -0
- ommlds/minichain/wrappers/stream.py +57 -0
- ommlds/nanochat/rustbpe/README.md +9 -0
- ommlds/nanochat/tokenizers.py +40 -6
- ommlds/specs/mcp/clients.py +146 -0
- ommlds/specs/mcp/protocol.py +123 -18
- ommlds/tools/git.py +82 -65
- {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/METADATA +13 -11
- ommlds-0.0.0.dev503.dist-info/RECORD +520 -0
- ommlds/cli/sessions/chat/chat/state/inject.py +0 -36
- ommlds/cli/sessions/chat/chat/user/inject.py +0 -62
- ommlds/cli/sessions/chat/chat/user/interactive.py +0 -31
- ommlds/cli/sessions/chat/chat/user/oneshot.py +0 -25
- ommlds/cli/sessions/chat/chat/user/types.py +0 -15
- ommlds/cli/sessions/chat/driver.py +0 -43
- ommlds/minichain/content/materialize.py +0 -196
- ommlds/minichain/content/simple.py +0 -47
- ommlds/minichain/content/transforms/base.py +0 -46
- ommlds/minichain/content/transforms/interleave.py +0 -70
- ommlds/minichain/content/transforms/squeeze.py +0 -72
- ommlds/minichain/content/transforms/strings.py +0 -24
- ommlds/minichain/content/types.py +0 -43
- ommlds/minichain/stream/wrap.py +0 -62
- ommlds-0.0.0.dev480.dist-info/RECORD +0 -427
- /ommlds/cli/sessions/chat/{chat → drivers}/__init__.py +0 -0
- /ommlds/cli/sessions/chat/{chat → drivers}/ai/__init__.py +0 -0
- /ommlds/cli/sessions/chat/{chat → drivers}/ai/injection.py +0 -0
- /ommlds/cli/sessions/chat/{chat/state → drivers/events}/__init__.py +0 -0
- /ommlds/cli/sessions/chat/{chat/user → drivers/phases}/__init__.py +0 -0
- /ommlds/cli/sessions/chat/{phases → drivers/phases}/inject.py +0 -0
- /ommlds/cli/sessions/chat/{phases → drivers/phases}/injection.py +0 -0
- /ommlds/cli/sessions/chat/{phases → drivers/phases}/manager.py +0 -0
- /ommlds/cli/sessions/chat/{phases → drivers/phases}/types.py +0 -0
- /ommlds/cli/sessions/chat/{phases → drivers/state}/__init__.py +0 -0
- /ommlds/cli/sessions/chat/{tools → drivers/tools}/__init__.py +0 -0
- /ommlds/cli/sessions/chat/{tools → drivers/tools}/fs/__init__.py +0 -0
- /ommlds/cli/sessions/chat/{tools → drivers/tools}/fs/configs.py +0 -0
- /ommlds/cli/sessions/chat/{tools → drivers/tools}/todo/__init__.py +0 -0
- /ommlds/cli/sessions/chat/{tools → drivers/tools}/todo/configs.py +0 -0
- /ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/__init__.py +0 -0
- /ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/configs.py +0 -0
- /ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/inject.py +0 -0
- /ommlds/{minichain/content/transforms → cli/sessions/chat/drivers/user}/__init__.py +0 -0
- {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/WHEEL +0 -0
- {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/entry_points.txt +0 -0
- {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/licenses/LICENSE +0 -0
- {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import typing as ta
|
|
4
|
+
import weakref
|
|
5
|
+
|
|
6
|
+
from omdev.tui import textual as tx
|
|
7
|
+
from omlish import check
|
|
8
|
+
from omlish import dataclasses as dc
|
|
9
|
+
from omlish import lang
|
|
10
|
+
from omlish.logs import all as logs
|
|
11
|
+
|
|
12
|
+
from ...... import minichain as mc
|
|
13
|
+
from .....backends.types import BackendName
|
|
14
|
+
from ...drivers.events.types import AiDeltaChatEvent
|
|
15
|
+
from ...drivers.events.types import AiMessagesChatEvent
|
|
16
|
+
from ...drivers.types import ChatDriver
|
|
17
|
+
from ...facades.facade import ChatFacade
|
|
18
|
+
from .styles import read_app_css
|
|
19
|
+
from .widgets.input import InputOuter
|
|
20
|
+
from .widgets.input import InputTextArea
|
|
21
|
+
from .widgets.messages import AiMessage
|
|
22
|
+
from .widgets.messages import MessagesContainer
|
|
23
|
+
from .widgets.messages import StaticAiMessage
|
|
24
|
+
from .widgets.messages import StreamAiMessage
|
|
25
|
+
from .widgets.messages import ToolConfirmationMessage
|
|
26
|
+
from .widgets.messages import UiMessage
|
|
27
|
+
from .widgets.messages import UserMessage
|
|
28
|
+
from .widgets.messages import WelcomeMessage
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
log, alog = logs.get_module_loggers(globals())
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
##
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
ChatEventQueue = ta.NewType('ChatEventQueue', asyncio.Queue)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
##
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ChatAppGetter(lang.AsyncCachedFunc0['ChatApp']):
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ChatApp(tx.App):
|
|
48
|
+
ENABLE_COMMAND_PALETTE: ta.ClassVar[bool] = False
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
*,
|
|
53
|
+
chat_facade: ChatFacade,
|
|
54
|
+
chat_driver: ChatDriver,
|
|
55
|
+
chat_event_queue: ChatEventQueue,
|
|
56
|
+
backend_name: BackendName | None = None,
|
|
57
|
+
devtools_setup: tx.DevtoolsSetup | None = None,
|
|
58
|
+
) -> None:
|
|
59
|
+
super().__init__()
|
|
60
|
+
|
|
61
|
+
if devtools_setup is not None:
|
|
62
|
+
devtools_setup(self)
|
|
63
|
+
|
|
64
|
+
self._chat_facade = chat_facade
|
|
65
|
+
self._chat_driver = chat_driver
|
|
66
|
+
self._chat_event_queue = chat_event_queue
|
|
67
|
+
self._backend_name = backend_name
|
|
68
|
+
|
|
69
|
+
self._chat_action_queue: asyncio.Queue[ta.Any] = asyncio.Queue()
|
|
70
|
+
|
|
71
|
+
self._input_focused_key_events: weakref.WeakSet[tx.Key] = weakref.WeakSet()
|
|
72
|
+
|
|
73
|
+
def get_driver_class(self) -> type[tx.Driver]:
|
|
74
|
+
return tx.get_pending_writes_driver_class(super().get_driver_class())
|
|
75
|
+
|
|
76
|
+
CSS: ta.ClassVar[str] = read_app_css()
|
|
77
|
+
|
|
78
|
+
#
|
|
79
|
+
|
|
80
|
+
def compose(self) -> tx.ComposeResult:
|
|
81
|
+
yield MessagesContainer(id='messages-container')
|
|
82
|
+
|
|
83
|
+
yield InputOuter(id='input-outer')
|
|
84
|
+
|
|
85
|
+
#
|
|
86
|
+
|
|
87
|
+
def _get_input_text_area(self) -> InputTextArea:
|
|
88
|
+
return self.query_one('#input', InputTextArea)
|
|
89
|
+
|
|
90
|
+
def _get_messages_container(self) -> tx.VerticalScroll:
|
|
91
|
+
return self.query_one('#messages-container', MessagesContainer)
|
|
92
|
+
|
|
93
|
+
#
|
|
94
|
+
|
|
95
|
+
def _is_messages_at_bottom(self, threshold: int = 3) -> bool:
|
|
96
|
+
return (ms := self._get_messages_container()).scroll_y >= (ms.max_scroll_y - threshold)
|
|
97
|
+
|
|
98
|
+
def _scroll_messages_to_bottom(self) -> None:
|
|
99
|
+
self._get_messages_container().scroll_end(animate=False)
|
|
100
|
+
|
|
101
|
+
def _anchor_messages(self) -> None:
|
|
102
|
+
if (ms := self._get_messages_container()).max_scroll_y:
|
|
103
|
+
ms.anchor()
|
|
104
|
+
|
|
105
|
+
#
|
|
106
|
+
|
|
107
|
+
_pending_mount_messages: list[tx.Widget] | None = None
|
|
108
|
+
|
|
109
|
+
async def _enqueue_mount_messages(self, *messages: tx.Widget) -> None:
|
|
110
|
+
if (lst := self._pending_mount_messages) is None:
|
|
111
|
+
lst = self._pending_mount_messages = []
|
|
112
|
+
|
|
113
|
+
lst.extend(messages)
|
|
114
|
+
|
|
115
|
+
_stream_ai_message: StreamAiMessage | None = None
|
|
116
|
+
|
|
117
|
+
async def _finalize_stream_ai_message(self) -> None:
|
|
118
|
+
if self._stream_ai_message is None:
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
await self._stream_ai_message.stop_stream()
|
|
122
|
+
self._stream_ai_message = None
|
|
123
|
+
|
|
124
|
+
async def _append_stream_ai_message_content(self, content: str) -> None:
|
|
125
|
+
if (sam := self._stream_ai_message) is not None:
|
|
126
|
+
was_at_bottom = self._is_messages_at_bottom()
|
|
127
|
+
|
|
128
|
+
await sam.append_content(content)
|
|
129
|
+
|
|
130
|
+
self.call_after_refresh(self._scroll_messages_to_bottom)
|
|
131
|
+
|
|
132
|
+
if was_at_bottom:
|
|
133
|
+
self.call_after_refresh(self._anchor_messages)
|
|
134
|
+
|
|
135
|
+
else:
|
|
136
|
+
await self._mount_messages(StreamAiMessage(content))
|
|
137
|
+
|
|
138
|
+
async def _mount_messages(self, *messages: tx.Widget) -> None:
|
|
139
|
+
was_at_bottom = self._is_messages_at_bottom()
|
|
140
|
+
|
|
141
|
+
msg_ctr = self._get_messages_container()
|
|
142
|
+
|
|
143
|
+
for msg in [*(self._pending_mount_messages or []), *messages]:
|
|
144
|
+
if isinstance(msg, (AiMessage, ToolConfirmationMessage)):
|
|
145
|
+
await self._finalize_stream_ai_message()
|
|
146
|
+
|
|
147
|
+
await msg_ctr.mount(msg)
|
|
148
|
+
|
|
149
|
+
if isinstance(msg, StreamAiMessage):
|
|
150
|
+
self._stream_ai_message = check.replacing_none(self._stream_ai_message, msg)
|
|
151
|
+
await msg.write_initial_content()
|
|
152
|
+
|
|
153
|
+
self._pending_mount_messages = None
|
|
154
|
+
|
|
155
|
+
self.call_after_refresh(self._scroll_messages_to_bottom)
|
|
156
|
+
|
|
157
|
+
if was_at_bottom:
|
|
158
|
+
self.call_after_refresh(self._anchor_messages)
|
|
159
|
+
|
|
160
|
+
#
|
|
161
|
+
|
|
162
|
+
_chat_event_task: asyncio.Task[None] | None = None
|
|
163
|
+
|
|
164
|
+
@logs.async_exception_logging(alog)
|
|
165
|
+
async def _chat_event_task_main(self) -> None:
|
|
166
|
+
while True:
|
|
167
|
+
ev = await self._chat_event_queue.get()
|
|
168
|
+
if ev is None:
|
|
169
|
+
break
|
|
170
|
+
|
|
171
|
+
await alog.debug(lambda: f'Got chat event: {ev!r}')
|
|
172
|
+
|
|
173
|
+
if isinstance(ev, AiMessagesChatEvent):
|
|
174
|
+
wx: list[tx.Widget] = []
|
|
175
|
+
|
|
176
|
+
for ai_msg in ev.chat:
|
|
177
|
+
if isinstance(ai_msg, mc.AiMessage):
|
|
178
|
+
wx.append(
|
|
179
|
+
StaticAiMessage(
|
|
180
|
+
check.isinstance(ai_msg.c, str),
|
|
181
|
+
markdown=True,
|
|
182
|
+
),
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
if wx:
|
|
186
|
+
await self._enqueue_mount_messages(*wx)
|
|
187
|
+
self.call_later(self._mount_messages)
|
|
188
|
+
|
|
189
|
+
elif isinstance(ev, AiDeltaChatEvent):
|
|
190
|
+
if isinstance(ev.delta, mc.ContentAiDelta):
|
|
191
|
+
cc = check.isinstance(ev.delta.c, str)
|
|
192
|
+
self.call_later(self._append_stream_ai_message_content, cc)
|
|
193
|
+
|
|
194
|
+
elif isinstance(ev.delta, mc.ToolUseAiDelta):
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
#
|
|
198
|
+
|
|
199
|
+
@dc.dataclass(frozen=True)
|
|
200
|
+
class UserInput:
|
|
201
|
+
text: str
|
|
202
|
+
|
|
203
|
+
_chat_action_task: asyncio.Task[None] | None = None
|
|
204
|
+
|
|
205
|
+
@logs.async_exception_logging(alog)
|
|
206
|
+
async def _chat_action_task_main(self) -> None:
|
|
207
|
+
while True:
|
|
208
|
+
ac = await self._chat_action_queue.get()
|
|
209
|
+
if ac is None:
|
|
210
|
+
break
|
|
211
|
+
|
|
212
|
+
await alog.debug(lambda: f'Got chat action: {ac!r}')
|
|
213
|
+
|
|
214
|
+
if isinstance(ac, ChatApp.UserInput):
|
|
215
|
+
try:
|
|
216
|
+
await self._chat_facade.handle_user_input(ac.text)
|
|
217
|
+
except Exception as e: # noqa
|
|
218
|
+
raise
|
|
219
|
+
|
|
220
|
+
else:
|
|
221
|
+
raise TypeError(ac) # noqa
|
|
222
|
+
|
|
223
|
+
#
|
|
224
|
+
|
|
225
|
+
async def on_mount(self) -> None:
|
|
226
|
+
check.state(self._chat_event_task is None)
|
|
227
|
+
self._chat_event_task = asyncio.create_task(self._chat_event_task_main())
|
|
228
|
+
|
|
229
|
+
await self._chat_driver.start()
|
|
230
|
+
|
|
231
|
+
check.state(self._chat_action_task is None)
|
|
232
|
+
self._chat_action_task = asyncio.create_task(self._chat_action_task_main())
|
|
233
|
+
|
|
234
|
+
self._get_input_text_area().focus()
|
|
235
|
+
|
|
236
|
+
await self._mount_messages(
|
|
237
|
+
WelcomeMessage('\n'.join([
|
|
238
|
+
f'Backend: {self._backend_name or "?"}',
|
|
239
|
+
f'Dir: {os.getcwd()}',
|
|
240
|
+
])),
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
async def on_unmount(self) -> None:
|
|
244
|
+
if (cdt := self._chat_event_task) is not None:
|
|
245
|
+
await self._chat_event_queue.put(None)
|
|
246
|
+
await cdt
|
|
247
|
+
|
|
248
|
+
await self._chat_driver.stop()
|
|
249
|
+
|
|
250
|
+
if (cet := self._chat_event_task) is not None:
|
|
251
|
+
await self._chat_event_queue.put(None)
|
|
252
|
+
await cet
|
|
253
|
+
|
|
254
|
+
@tx.on(InputTextArea.Submitted)
|
|
255
|
+
async def on_input_text_area_submitted(self, event: InputTextArea.Submitted) -> None:
|
|
256
|
+
self._get_input_text_area().clear()
|
|
257
|
+
|
|
258
|
+
await self._finalize_stream_ai_message()
|
|
259
|
+
|
|
260
|
+
await self._mount_messages(
|
|
261
|
+
UserMessage(
|
|
262
|
+
event.text,
|
|
263
|
+
),
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
await self._chat_action_queue.put(ChatApp.UserInput(event.text))
|
|
267
|
+
|
|
268
|
+
@tx.on(tx.Key)
|
|
269
|
+
async def on_key(self, event: tx.Key) -> None:
|
|
270
|
+
if event in self._input_focused_key_events:
|
|
271
|
+
return
|
|
272
|
+
|
|
273
|
+
chat_input = self._get_input_text_area()
|
|
274
|
+
|
|
275
|
+
if not chat_input.has_focus:
|
|
276
|
+
self._input_focused_key_events.add(event)
|
|
277
|
+
|
|
278
|
+
chat_input.focus()
|
|
279
|
+
|
|
280
|
+
self.screen.post_message(tx.Key(event.key, event.character))
|
|
281
|
+
|
|
282
|
+
#
|
|
283
|
+
|
|
284
|
+
async def confirm_tool_use(
|
|
285
|
+
self,
|
|
286
|
+
outer_message: str,
|
|
287
|
+
inner_message: str,
|
|
288
|
+
) -> bool:
|
|
289
|
+
fut: asyncio.Future[bool] = asyncio.get_running_loop().create_future()
|
|
290
|
+
|
|
291
|
+
tcm = ToolConfirmationMessage(
|
|
292
|
+
outer_message,
|
|
293
|
+
inner_message,
|
|
294
|
+
fut,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
async def inner() -> None:
|
|
298
|
+
await self._mount_messages(tcm)
|
|
299
|
+
|
|
300
|
+
self.call_later(inner)
|
|
301
|
+
|
|
302
|
+
ret = await fut
|
|
303
|
+
|
|
304
|
+
return ret
|
|
305
|
+
|
|
306
|
+
async def display_ui_message(
|
|
307
|
+
self,
|
|
308
|
+
content: str,
|
|
309
|
+
) -> None:
|
|
310
|
+
await self._mount_messages(UiMessage(content))
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from ...facades.ui import UiMessageDisplayer
|
|
2
|
+
from .app import ChatAppGetter
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ChatAppUiMessageDisplayer(UiMessageDisplayer):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
*,
|
|
12
|
+
app: ChatAppGetter,
|
|
13
|
+
) -> None:
|
|
14
|
+
super().__init__()
|
|
15
|
+
|
|
16
|
+
self._app = app
|
|
17
|
+
|
|
18
|
+
async def display_ui_message(self, content: str) -> None:
|
|
19
|
+
await (await self._app()).display_ui_message(content)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FIXME:
|
|
3
|
+
- too lazy to lazy import guts like every other proper inject module lol >_<
|
|
4
|
+
"""
|
|
5
|
+
import asyncio
|
|
6
|
+
import contextlib
|
|
7
|
+
|
|
8
|
+
from omlish import inject as inj
|
|
9
|
+
from omlish import lang
|
|
10
|
+
|
|
11
|
+
from ...drivers.events.injection import event_callbacks
|
|
12
|
+
from ..base import ChatInterface
|
|
13
|
+
from .configs import TextualInterfaceConfig
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
with lang.auto_proxy_import(globals()):
|
|
17
|
+
from omdev.tui import textual as tx
|
|
18
|
+
|
|
19
|
+
from ...drivers.tools import confirmation as _tools_confirmation
|
|
20
|
+
from ...facades import ui as _facades_ui
|
|
21
|
+
from . import app as _app
|
|
22
|
+
from . import facades as _facades
|
|
23
|
+
from . import interface as _interface
|
|
24
|
+
from . import tools as _tools
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
##
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def bind_textual(cfg: TextualInterfaceConfig = TextualInterfaceConfig()) -> inj.Elements:
|
|
31
|
+
els: list[inj.Elemental] = [
|
|
32
|
+
inj.bind(ChatInterface, to_ctor=_interface.TextualChatInterface, singleton=True),
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
#
|
|
36
|
+
|
|
37
|
+
els.extend([
|
|
38
|
+
inj.bind(_app.ChatApp, singleton=True),
|
|
39
|
+
inj.bind_async_late(_app.ChatApp, _app.ChatAppGetter),
|
|
40
|
+
])
|
|
41
|
+
|
|
42
|
+
#
|
|
43
|
+
|
|
44
|
+
els.extend([
|
|
45
|
+
inj.bind(_app.ChatEventQueue, to_const=asyncio.Queue()),
|
|
46
|
+
|
|
47
|
+
event_callbacks().bind_item(to_fn=inj.target(eq=_app.ChatEventQueue)(lambda eq: lambda ev: eq.put(ev))),
|
|
48
|
+
])
|
|
49
|
+
|
|
50
|
+
#
|
|
51
|
+
|
|
52
|
+
if cfg.enable_tools:
|
|
53
|
+
if cfg.dangerous_no_tool_confirmation:
|
|
54
|
+
els.append(inj.bind(
|
|
55
|
+
_tools_confirmation.ToolExecutionConfirmation,
|
|
56
|
+
to_ctor=_tools_confirmation.UnsafeAlwaysAllowToolExecutionConfirmation,
|
|
57
|
+
singleton=True,
|
|
58
|
+
))
|
|
59
|
+
|
|
60
|
+
else:
|
|
61
|
+
els.append(inj.bind(
|
|
62
|
+
_tools_confirmation.ToolExecutionConfirmation,
|
|
63
|
+
to_ctor=_tools.ChatAppToolExecutionConfirmation,
|
|
64
|
+
singleton=True,
|
|
65
|
+
))
|
|
66
|
+
|
|
67
|
+
#
|
|
68
|
+
|
|
69
|
+
els.extend([
|
|
70
|
+
inj.bind(tx.DevtoolsConfig(port=41932)), # FIXME: lol
|
|
71
|
+
|
|
72
|
+
inj.bind(
|
|
73
|
+
tx.DevtoolsManager,
|
|
74
|
+
singleton=True,
|
|
75
|
+
to_async_fn=inj.make_async_managed_provider(
|
|
76
|
+
tx.DevtoolsManager,
|
|
77
|
+
contextlib.aclosing,
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
|
|
81
|
+
inj.bind(
|
|
82
|
+
tx.DevtoolsSetup,
|
|
83
|
+
to_async_fn=inj.target(mgr=tx.DevtoolsManager)(lambda mgr: mgr.get_setup()),
|
|
84
|
+
singleton=True,
|
|
85
|
+
),
|
|
86
|
+
])
|
|
87
|
+
|
|
88
|
+
#
|
|
89
|
+
|
|
90
|
+
els.extend([
|
|
91
|
+
inj.bind(_facades.ChatAppUiMessageDisplayer, singleton=True),
|
|
92
|
+
inj.bind(_facades_ui.UiMessageDisplayer, to_key=_facades.ChatAppUiMessageDisplayer),
|
|
93
|
+
])
|
|
94
|
+
|
|
95
|
+
#
|
|
96
|
+
|
|
97
|
+
return inj.as_elements(*els)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from omdev.tui import textual as tx
|
|
2
|
+
|
|
3
|
+
from ..base import ChatInterface
|
|
4
|
+
from .app import ChatApp
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
##
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TextualChatInterface(ChatInterface):
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
*,
|
|
14
|
+
app: ChatApp,
|
|
15
|
+
) -> None:
|
|
16
|
+
super().__init__()
|
|
17
|
+
|
|
18
|
+
self._app = app
|
|
19
|
+
|
|
20
|
+
async def run(self) -> None:
|
|
21
|
+
# FIXME: move lol
|
|
22
|
+
tx.set_root_logger_to_devtools(self._app.devtools)
|
|
23
|
+
|
|
24
|
+
await self._app.run_async()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import io as _io
|
|
2
|
+
|
|
3
|
+
from omlish import lang as _lang
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@_lang.cached_function
|
|
10
|
+
def read_app_css() -> str:
|
|
11
|
+
tcss_rsrcs = [
|
|
12
|
+
rsrc
|
|
13
|
+
for rsrc in _lang.get_relative_resources(globals=globals()).values()
|
|
14
|
+
if rsrc.name.endswith('.tcss')
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
out = _io.StringIO()
|
|
18
|
+
|
|
19
|
+
for i, rsrc in enumerate(tcss_rsrcs):
|
|
20
|
+
if i:
|
|
21
|
+
out.write('\n\n')
|
|
22
|
+
|
|
23
|
+
out.write(f'/*** {rsrc.name} ***/\n')
|
|
24
|
+
out.write('\n')
|
|
25
|
+
|
|
26
|
+
out.write(rsrc.read_text().strip())
|
|
27
|
+
out.write('\n')
|
|
28
|
+
|
|
29
|
+
return out.getvalue()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#input-outer {
|
|
2
|
+
width: 100%;
|
|
3
|
+
height: auto;
|
|
4
|
+
|
|
5
|
+
background: $background-darken-3;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
#input-vertical {
|
|
9
|
+
width: 100%;
|
|
10
|
+
height: auto;
|
|
11
|
+
|
|
12
|
+
margin: 0 1 1 1;
|
|
13
|
+
|
|
14
|
+
padding: 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#input-vertical2 {
|
|
18
|
+
width: 100%;
|
|
19
|
+
height: auto;
|
|
20
|
+
|
|
21
|
+
border: round $foreground-muted;
|
|
22
|
+
|
|
23
|
+
padding: 0 1;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#input-horizontal {
|
|
27
|
+
width: 100%;
|
|
28
|
+
height: auto;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#input-glyph {
|
|
32
|
+
width: auto;
|
|
33
|
+
|
|
34
|
+
padding: 0 1 0 0;
|
|
35
|
+
|
|
36
|
+
background: transparent;
|
|
37
|
+
color: $primary;
|
|
38
|
+
|
|
39
|
+
text-style: bold;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#input {
|
|
43
|
+
width: 1fr;
|
|
44
|
+
height: auto;
|
|
45
|
+
max-height: 16;
|
|
46
|
+
|
|
47
|
+
border: none;
|
|
48
|
+
|
|
49
|
+
padding: 0;
|
|
50
|
+
|
|
51
|
+
background: transparent;
|
|
52
|
+
color: $text;
|
|
53
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/* Container */
|
|
2
|
+
|
|
3
|
+
#messages-container {
|
|
4
|
+
width: 100%;
|
|
5
|
+
height: 1fr;
|
|
6
|
+
|
|
7
|
+
scrollbar-gutter: stable;
|
|
8
|
+
|
|
9
|
+
background: $background-darken-3;
|
|
10
|
+
|
|
11
|
+
text-align: left;
|
|
12
|
+
|
|
13
|
+
scrollbar-size: 1 1;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
/* Base */
|
|
18
|
+
|
|
19
|
+
.message {
|
|
20
|
+
width: 1fr;
|
|
21
|
+
height: auto;
|
|
22
|
+
|
|
23
|
+
margin: 1 0 0 0;
|
|
24
|
+
|
|
25
|
+
padding-right: 1;
|
|
26
|
+
|
|
27
|
+
layout: stream;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.message-glyph {
|
|
31
|
+
width: auto;
|
|
32
|
+
height: auto;
|
|
33
|
+
|
|
34
|
+
background: transparent;
|
|
35
|
+
color: $primary;
|
|
36
|
+
|
|
37
|
+
text-style: bold;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.message-outer {
|
|
41
|
+
width: 1fr;
|
|
42
|
+
height: auto;
|
|
43
|
+
|
|
44
|
+
align: left top;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.message-inner {
|
|
48
|
+
width: 1fr;
|
|
49
|
+
height: auto;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
/* Welcome */
|
|
54
|
+
|
|
55
|
+
.welcome-message {
|
|
56
|
+
margin: 0;
|
|
57
|
+
|
|
58
|
+
padding: 1 2 1 1;
|
|
59
|
+
|
|
60
|
+
border: round;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.welcome-message-outer {
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.welcome-message-content {
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
/* User */
|
|
71
|
+
|
|
72
|
+
.user-message {
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.user-message-outer {
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.user-message-glyph {
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.user-message-inner {
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
/* Ai */
|
|
86
|
+
|
|
87
|
+
.ai-message {
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.ai-message-outer {
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.ai-message-glyph {
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.ai-message-inner {
|
|
97
|
+
padding: 0;
|
|
98
|
+
|
|
99
|
+
Markdown {
|
|
100
|
+
width: 100%;
|
|
101
|
+
height: auto;
|
|
102
|
+
|
|
103
|
+
margin: 0;
|
|
104
|
+
padding: 0;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
/* Tool Confirmation */
|
|
110
|
+
|
|
111
|
+
.tool-confirmation-message {
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.tool-confirmation-message-outer {
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.tool-confirmation-message-glyph {
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.tool-confirmation-message-inner {
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.tool-confirmation-message-inner-open {
|
|
124
|
+
background: $warning-lighten-3 15%;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.tool-confirmation-message-inner-closed {
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.tool-confirmation-message-outer-content {
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.tool-confirmation-message-inner-content {
|
|
134
|
+
background: $background;
|
|
135
|
+
|
|
136
|
+
margin: 1;
|
|
137
|
+
padding: 1;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.tool-confirmation-message-controls {
|
|
141
|
+
margin: 1;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
/* Ui */
|
|
146
|
+
|
|
147
|
+
.ui-message {
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.ui-message-glyph {
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.ui-message-outer {
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.ui-message-inner {
|
|
157
|
+
}
|