rcas 0.2.0
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.
- checksums.yaml +7 -0
- data/CITATION.cff +17 -0
- data/DESIGN.md +783 -0
- data/LICENSE +21 -0
- data/MANUAL.md +6265 -0
- data/README.md +267 -0
- data/bin/rcas +9 -0
- data/bin/rcas-app +9 -0
- data/bin/rcas-chat +9 -0
- data/lib/rcas/algebraic.rb +481 -0
- data/lib/rcas/analysis.rb +966 -0
- data/lib/rcas/app/launcher.rb +203 -0
- data/lib/rcas/app/public/app.css +402 -0
- data/lib/rcas/app/public/app.js +449 -0
- data/lib/rcas/app/public/index.html +46 -0
- data/lib/rcas/app/server.rb +220 -0
- data/lib/rcas/app/window.rb +94 -0
- data/lib/rcas/app/worksheet.rb +290 -0
- data/lib/rcas/app.rb +168 -0
- data/lib/rcas/background.rb +758 -0
- data/lib/rcas/chat/assistant.rb +199 -0
- data/lib/rcas/chat/picker.rb +164 -0
- data/lib/rcas/chat/repl.rb +583 -0
- data/lib/rcas/chat/session.rb +137 -0
- data/lib/rcas/chat/settings.rb +71 -0
- data/lib/rcas/chat/style.rb +30 -0
- data/lib/rcas/chat/tool.rb +53 -0
- data/lib/rcas/chat/ui.rb +316 -0
- data/lib/rcas/chat/usage.rb +62 -0
- data/lib/rcas/chat/workspace.rb +132 -0
- data/lib/rcas/chat.rb +54 -0
- data/lib/rcas/coefficients.rb +170 -0
- data/lib/rcas/combinatorics.rb +274 -0
- data/lib/rcas/complex_parts.rb +160 -0
- data/lib/rcas/constants.rb +129 -0
- data/lib/rcas/core_ext.rb +35 -0
- data/lib/rcas/decide.rb +501 -0
- data/lib/rcas/decompositions.rb +241 -0
- data/lib/rcas/differentiate.rb +144 -0
- data/lib/rcas/discussion.rb +558 -0
- data/lib/rcas/distributions.rb +980 -0
- data/lib/rcas/dixon.rb +95 -0
- data/lib/rcas/docs.rb +321 -0
- data/lib/rcas/domains.rb +728 -0
- data/lib/rcas/expand.rb +174 -0
- data/lib/rcas/expression.rb +613 -0
- data/lib/rcas/factor.rb +605 -0
- data/lib/rcas/finite_field.rb +577 -0
- data/lib/rcas/fourier.rb +118 -0
- data/lib/rcas/fps.rb +678 -0
- data/lib/rcas/fraction.rb +126 -0
- data/lib/rcas/functions.rb +1136 -0
- data/lib/rcas/gcd.rb +112 -0
- data/lib/rcas/geometry.rb +266 -0
- data/lib/rcas/groebner.rb +162 -0
- data/lib/rcas/hold.rb +277 -0
- data/lib/rcas/hypothesis.rb +364 -0
- data/lib/rcas/inequalities.rb +689 -0
- data/lib/rcas/integral_functions.rb +260 -0
- data/lib/rcas/integrate.rb +1589 -0
- data/lib/rcas/integrate_substitutions.rb +434 -0
- data/lib/rcas/interpolate.rb +40 -0
- data/lib/rcas/irb.rb +146 -0
- data/lib/rcas/laplace.rb +159 -0
- data/lib/rcas/latex.rb +556 -0
- data/lib/rcas/lattice.rb +172 -0
- data/lib/rcas/linear_algebra.rb +117 -0
- data/lib/rcas/linear_program.rb +416 -0
- data/lib/rcas/lint.rb +79 -0
- data/lib/rcas/matrix.rb +531 -0
- data/lib/rcas/matrix_multiply.rb +202 -0
- data/lib/rcas/multimodular.rb +286 -0
- data/lib/rcas/named_polynomials.rb +274 -0
- data/lib/rcas/number_theory.rb +443 -0
- data/lib/rcas/numerics.rb +825 -0
- data/lib/rcas/ode.rb +488 -0
- data/lib/rcas/openmath/objects.rb +364 -0
- data/lib/rcas/openmath/phrasebook.rb +551 -0
- data/lib/rcas/openmath/popcorn.rb +518 -0
- data/lib/rcas/openmath/xml.rb +309 -0
- data/lib/rcas/openmath.rb +49 -0
- data/lib/rcas/petkovsek.rb +165 -0
- data/lib/rcas/piecewise.rb +488 -0
- data/lib/rcas/plot.rb +763 -0
- data/lib/rcas/plot3d.rb +419 -0
- data/lib/rcas/poly_matrix.rb +318 -0
- data/lib/rcas/poly_recurrence.rb +117 -0
- data/lib/rcas/polynomial.rb +466 -0
- data/lib/rcas/precision.rb +925 -0
- data/lib/rcas/printer.rb +150 -0
- data/lib/rcas/product.rb +155 -0
- data/lib/rcas/q_difference.rb +296 -0
- data/lib/rcas/q_functions.rb +158 -0
- data/lib/rcas/q_summation.rb +308 -0
- data/lib/rcas/q_zeilberger.rb +199 -0
- data/lib/rcas/random.rb +506 -0
- data/lib/rcas/rational_function.rb +186 -0
- data/lib/rcas/recurrence.rb +323 -0
- data/lib/rcas/render.rb +431 -0
- data/lib/rcas/results.rb +192 -0
- data/lib/rcas/scalar.rb +219 -0
- data/lib/rcas/series.rb +726 -0
- data/lib/rcas/simplify.rb +649 -0
- data/lib/rcas/solve.rb +2002 -0
- data/lib/rcas/special.rb +163 -0
- data/lib/rcas/statistics.rb +175 -0
- data/lib/rcas/steps.rb +835 -0
- data/lib/rcas/summation.rb +532 -0
- data/lib/rcas/trig.rb +264 -0
- data/lib/rcas/van_hoeij.rb +241 -0
- data/lib/rcas/vector.rb +175 -0
- data/lib/rcas/vector_calculus.rb +411 -0
- data/lib/rcas/version.rb +5 -0
- data/lib/rcas/zeilberger.rb +358 -0
- data/lib/rcas.rb +91 -0
- data/package.json +8 -0
- metadata +206 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "rbconfig"
|
|
5
|
+
|
|
6
|
+
module RCAS
|
|
7
|
+
module App
|
|
8
|
+
# The desktop entries that make rcas-app look like an installed
|
|
9
|
+
# application: an icon in the Dock, in the Start menu or in the
|
|
10
|
+
# activities overview, that starts the program without a terminal.
|
|
11
|
+
#
|
|
12
|
+
# bin/rcas-app --install # put them where the desktop looks
|
|
13
|
+
# bin/rcas-app --uninstall # take them away again
|
|
14
|
+
#
|
|
15
|
+
# Each platform wants a different thing and each is a few lines of
|
|
16
|
+
# text, so all three are written here rather than shipped as files:
|
|
17
|
+
#
|
|
18
|
+
# * macOS: an application bundle, rcas.app in ~/Applications, whose
|
|
19
|
+
# executable is a shell script that runs bin/rcas-app. `sips` and
|
|
20
|
+
# `iconutil` (both part of macOS) turn the logo into the .icns the
|
|
21
|
+
# Dock wants; without them the bundle simply has the default icon.
|
|
22
|
+
# * Linux: a .desktop entry [FDO14] in ~/.local/share/applications.
|
|
23
|
+
# StartupWMClass matches the --class=rcas the window is opened with,
|
|
24
|
+
# so the running window is grouped under this icon and not under the
|
|
25
|
+
# browser's.
|
|
26
|
+
# * Windows: a shortcut in the Start menu, written by the one piece of
|
|
27
|
+
# Windows scripting that is always installed (WScript.Shell through
|
|
28
|
+
# PowerShell); if that fails, a .cmd file in the same place, which
|
|
29
|
+
# works as well and only looks plainer.
|
|
30
|
+
module Launcher
|
|
31
|
+
IDENTIFIER = "org.rcas.app"
|
|
32
|
+
|
|
33
|
+
module_function
|
|
34
|
+
|
|
35
|
+
def macos? = Window.macos?
|
|
36
|
+
def windows? = Window.windows?
|
|
37
|
+
|
|
38
|
+
# Where the entry goes, per platform.
|
|
39
|
+
def destination
|
|
40
|
+
if macos?
|
|
41
|
+
File.join(Dir.home, "Applications", "rcas.app")
|
|
42
|
+
elsif windows?
|
|
43
|
+
File.join(start_menu, "rcas.lnk")
|
|
44
|
+
else
|
|
45
|
+
File.join(ENV.fetch("XDG_DATA_HOME", File.join(Dir.home, ".local", "share")), "applications", "rcas.desktop")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def start_menu
|
|
50
|
+
base = ENV["APPDATA"] || File.join(Dir.home, "AppData", "Roaming")
|
|
51
|
+
File.join(base, "Microsoft", "Windows", "Start Menu", "Programs")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# The command the entry runs: this Ruby, this checkout.
|
|
55
|
+
def command = [RbConfig.ruby, File.join(App.root, "bin", "rcas-app")]
|
|
56
|
+
|
|
57
|
+
def install
|
|
58
|
+
path = destination
|
|
59
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
60
|
+
if macos? then install_bundle(path)
|
|
61
|
+
elsif windows? then install_shortcut(path)
|
|
62
|
+
else install_desktop_entry(path)
|
|
63
|
+
end
|
|
64
|
+
path
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def uninstall
|
|
68
|
+
path = destination
|
|
69
|
+
return nil unless File.exist?(path)
|
|
70
|
+
FileUtils.rm_rf(path)
|
|
71
|
+
alternative = windows? ? path.sub(/\.lnk\z/, ".cmd") : nil
|
|
72
|
+
FileUtils.rm_f(alternative) if alternative
|
|
73
|
+
path
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# ---- macOS ---------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
def install_bundle(bundle)
|
|
79
|
+
FileUtils.rm_rf(bundle)
|
|
80
|
+
FileUtils.mkdir_p(File.join(bundle, "Contents", "MacOS"))
|
|
81
|
+
FileUtils.mkdir_p(File.join(bundle, "Contents", "Resources"))
|
|
82
|
+
File.write(File.join(bundle, "Contents", "Info.plist"), plist)
|
|
83
|
+
runner = File.join(bundle, "Contents", "MacOS", "rcas")
|
|
84
|
+
ruby, script = command
|
|
85
|
+
File.write(runner, "#!/bin/sh\nexec #{quote(ruby)} #{quote(script)} \"$@\"\n")
|
|
86
|
+
File.chmod(0o755, runner)
|
|
87
|
+
icns(File.join(bundle, "Contents", "Resources", "rcas.icns"))
|
|
88
|
+
bundle
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def plist
|
|
92
|
+
<<~XML
|
|
93
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
94
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
95
|
+
<plist version="1.0">
|
|
96
|
+
<dict>
|
|
97
|
+
<key>CFBundleName</key><string>rcas</string>
|
|
98
|
+
<key>CFBundleDisplayName</key><string>rcas</string>
|
|
99
|
+
<key>CFBundleExecutable</key><string>rcas</string>
|
|
100
|
+
<key>CFBundleIdentifier</key><string>#{IDENTIFIER}</string>
|
|
101
|
+
<key>CFBundleIconFile</key><string>rcas</string>
|
|
102
|
+
<key>CFBundlePackageType</key><string>APPL</string>
|
|
103
|
+
<key>CFBundleShortVersionString</key><string>#{RCAS::VERSION}</string>
|
|
104
|
+
<key>CFBundleVersion</key><string>#{RCAS::VERSION}</string>
|
|
105
|
+
<key>NSHighResolutionCapable</key><true/>
|
|
106
|
+
<key>LSMinimumSystemVersion</key><string>10.13</string>
|
|
107
|
+
</dict>
|
|
108
|
+
</plist>
|
|
109
|
+
XML
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# The logo as an .icns, if macOS's own tools are there to convert it.
|
|
113
|
+
def icns(path)
|
|
114
|
+
logo = App.logo
|
|
115
|
+
sips = Render.which("sips")
|
|
116
|
+
iconutil = Render.which("iconutil")
|
|
117
|
+
return nil unless logo && sips && iconutil
|
|
118
|
+
Dir.mktmpdir("rcas-icon") do |dir|
|
|
119
|
+
iconset = File.join(dir, "rcas.iconset")
|
|
120
|
+
FileUtils.mkdir_p(iconset)
|
|
121
|
+
# Two things iconutil is strict about: the files must really be
|
|
122
|
+
# PNG (sips goes by -s format, not by the name it is given) and
|
|
123
|
+
# each must be exactly square, which -Z and --padToHeightWidth do
|
|
124
|
+
# together without squashing a logo that is wider than it is tall.
|
|
125
|
+
[16, 32, 128, 256, 512].each do |size|
|
|
126
|
+
{ "" => size, "@2x" => size * 2 }.each do |suffix, pixels|
|
|
127
|
+
Render.run(sips, "-s", "format", "png", "-Z", pixels.to_s,
|
|
128
|
+
"--padToHeightWidth", pixels.to_s, pixels.to_s, "--padColor", "FFFFFF",
|
|
129
|
+
logo, "--out", File.join(iconset, "icon_#{size}x#{size}#{suffix}.png"))
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
Render.run(iconutil, "-c", "icns", iconset, "-o", path)
|
|
133
|
+
end
|
|
134
|
+
path
|
|
135
|
+
rescue Render::Error, SystemCallError
|
|
136
|
+
nil # an application without an icon still runs
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# ---- Linux ---------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
def install_desktop_entry(path)
|
|
142
|
+
File.write(path, desktop_entry)
|
|
143
|
+
File.chmod(0o644, path)
|
|
144
|
+
path
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def desktop_entry
|
|
148
|
+
ruby, script = command
|
|
149
|
+
<<~ENTRY
|
|
150
|
+
[Desktop Entry]
|
|
151
|
+
Type=Application
|
|
152
|
+
Version=1.0
|
|
153
|
+
Name=rcas
|
|
154
|
+
GenericName=Computer Algebra System
|
|
155
|
+
Comment=Exact mathematics: algebra, calculus, plots
|
|
156
|
+
Exec=#{quote(ruby)} #{quote(script)}
|
|
157
|
+
Icon=#{App.logo || 'accessories-calculator'}
|
|
158
|
+
Terminal=false
|
|
159
|
+
Categories=Education;Science;Math;
|
|
160
|
+
Keywords=algebra;calculus;mathematics;cas;
|
|
161
|
+
StartupWMClass=rcas
|
|
162
|
+
ENTRY
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# ---- Windows -------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
def install_shortcut(path)
|
|
168
|
+
ruby, script = command
|
|
169
|
+
powershell = Render.which("powershell.exe", "powershell", "pwsh.exe", "pwsh")
|
|
170
|
+
if powershell
|
|
171
|
+
begin
|
|
172
|
+
Render.run(powershell, "-NoProfile", "-NonInteractive", "-Command", shortcut_script(path, ruby, script))
|
|
173
|
+
return path if File.exist?(path)
|
|
174
|
+
rescue Render::Error
|
|
175
|
+
nil # fall through to the .cmd
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
batch = path.sub(/\.lnk\z/, ".cmd")
|
|
179
|
+
File.write(batch, "@echo off\r\nstart \"rcas\" /b #{quote(ruby)} #{quote(script)} %*\r\n")
|
|
180
|
+
batch
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def shortcut_script(path, ruby, script)
|
|
184
|
+
# rubyw runs without a console window; plain ruby is the fallback.
|
|
185
|
+
runner = ruby.sub(/ruby\.exe\z/i, "rubyw.exe")
|
|
186
|
+
runner = ruby unless File.exist?(runner)
|
|
187
|
+
<<~PS.gsub("\n", "; ")
|
|
188
|
+
$s = (New-Object -ComObject WScript.Shell).CreateShortcut('#{path}')
|
|
189
|
+
$s.TargetPath = '#{runner}'
|
|
190
|
+
$s.Arguments = '"#{script}"'
|
|
191
|
+
$s.WorkingDirectory = '#{App.root}'
|
|
192
|
+
$s.IconLocation = '#{App.logo}'
|
|
193
|
+
$s.Description = 'rcas - computer algebra'
|
|
194
|
+
$s.Save()
|
|
195
|
+
PS
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# ---- helpers -------------------------------------------------------------
|
|
199
|
+
|
|
200
|
+
def quote(word) = word.to_s.match?(/\A[\w.\/:\\-]+\z/) ? word.to_s : %("#{word}")
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/* The window: a worksheet of In/Out cells. Light and dark are the same
|
|
2
|
+
design with the palette swapped; the whole palette is defined on :root
|
|
3
|
+
so that either media query or [data-theme] can redefine it. */
|
|
4
|
+
|
|
5
|
+
:root {
|
|
6
|
+
color-scheme: light dark;
|
|
7
|
+
|
|
8
|
+
--ground: #fbfbfa;
|
|
9
|
+
--panel: #ffffff;
|
|
10
|
+
--sunken: #f3f3f1;
|
|
11
|
+
--ink: #1c1b19;
|
|
12
|
+
--ink-dim: #6b6a66;
|
|
13
|
+
--ink-faint: #9b9a95;
|
|
14
|
+
--rule: #e4e3df;
|
|
15
|
+
--accent: #7c3a1d;
|
|
16
|
+
--accent-soft: #f0e6e0;
|
|
17
|
+
--result: #1f5c3d;
|
|
18
|
+
--danger: #a32020;
|
|
19
|
+
--link: #2b5fa8;
|
|
20
|
+
|
|
21
|
+
--mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
|
|
22
|
+
--sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
|
|
23
|
+
--gutter: 20px;
|
|
24
|
+
--label: 4.5rem;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@media (prefers-color-scheme: dark) {
|
|
28
|
+
:root:not([data-theme="light"]) {
|
|
29
|
+
--ground: #17171a;
|
|
30
|
+
--panel: #1e1e22;
|
|
31
|
+
--sunken: #26262b;
|
|
32
|
+
--ink: #e8e7e3;
|
|
33
|
+
--ink-dim: #a3a29c;
|
|
34
|
+
--ink-faint: #74736e;
|
|
35
|
+
--rule: #32323a;
|
|
36
|
+
--accent: #e0a37a;
|
|
37
|
+
--accent-soft: #2c2520;
|
|
38
|
+
--result: #7fcfa3;
|
|
39
|
+
--danger: #ef8a8a;
|
|
40
|
+
--link: #8ab4f8;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
:root[data-theme="dark"] {
|
|
45
|
+
--ground: #17171a;
|
|
46
|
+
--panel: #1e1e22;
|
|
47
|
+
--sunken: #26262b;
|
|
48
|
+
--ink: #e8e7e3;
|
|
49
|
+
--ink-dim: #a3a29c;
|
|
50
|
+
--ink-faint: #74736e;
|
|
51
|
+
--rule: #32323a;
|
|
52
|
+
--accent: #e0a37a;
|
|
53
|
+
--accent-soft: #2c2520;
|
|
54
|
+
--result: #7fcfa3;
|
|
55
|
+
--danger: #ef8a8a;
|
|
56
|
+
--link: #8ab4f8;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
* { box-sizing: border-box; }
|
|
60
|
+
|
|
61
|
+
html, body {
|
|
62
|
+
height: 100%;
|
|
63
|
+
margin: 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
body {
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
background: var(--ground);
|
|
70
|
+
color: var(--ink);
|
|
71
|
+
font-family: var(--sans);
|
|
72
|
+
font-size: 14px;
|
|
73
|
+
line-height: 1.55;
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* ---- title bar ---------------------------------------------------------- */
|
|
78
|
+
|
|
79
|
+
.titlebar {
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
gap: 10px;
|
|
83
|
+
flex: none;
|
|
84
|
+
padding: 10px var(--gutter);
|
|
85
|
+
padding-top: calc(10px + env(safe-area-inset-top, 0px));
|
|
86
|
+
border-bottom: 1px solid var(--rule);
|
|
87
|
+
background: var(--panel);
|
|
88
|
+
user-select: none;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.titlebar .logo {
|
|
92
|
+
width: 22px;
|
|
93
|
+
height: 22px;
|
|
94
|
+
border-radius: 5px;
|
|
95
|
+
object-fit: cover;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.titlebar h1 {
|
|
99
|
+
margin: 0;
|
|
100
|
+
font-size: 15px;
|
|
101
|
+
font-weight: 600;
|
|
102
|
+
letter-spacing: 0.01em;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.titlebar .version { color: var(--ink-faint); font-size: 12px; }
|
|
106
|
+
.titlebar .spacer { flex: 1; }
|
|
107
|
+
|
|
108
|
+
.modes {
|
|
109
|
+
display: flex;
|
|
110
|
+
gap: 2px;
|
|
111
|
+
padding: 2px;
|
|
112
|
+
border-radius: 7px;
|
|
113
|
+
background: var(--sunken);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.modes button {
|
|
117
|
+
padding: 3px 9px;
|
|
118
|
+
border: 0;
|
|
119
|
+
border-radius: 5px;
|
|
120
|
+
background: transparent;
|
|
121
|
+
color: var(--ink-dim);
|
|
122
|
+
font: inherit;
|
|
123
|
+
font-size: 12px;
|
|
124
|
+
cursor: pointer;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.modes button[aria-pressed="true"] {
|
|
128
|
+
background: var(--panel);
|
|
129
|
+
color: var(--ink);
|
|
130
|
+
box-shadow: 0 1px 2px rgb(0 0 0 / 0.12);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
button.ghost {
|
|
134
|
+
width: 26px;
|
|
135
|
+
height: 26px;
|
|
136
|
+
border: 1px solid var(--rule);
|
|
137
|
+
border-radius: 50%;
|
|
138
|
+
background: transparent;
|
|
139
|
+
color: var(--ink-dim);
|
|
140
|
+
font: inherit;
|
|
141
|
+
cursor: pointer;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
button.ghost:hover, .modes button:hover { color: var(--ink); }
|
|
145
|
+
|
|
146
|
+
/* ---- worksheet ---------------------------------------------------------- */
|
|
147
|
+
|
|
148
|
+
.worksheet {
|
|
149
|
+
flex: 1;
|
|
150
|
+
overflow-y: auto;
|
|
151
|
+
overscroll-behavior: contain;
|
|
152
|
+
padding: 18px var(--gutter) 8px;
|
|
153
|
+
outline: none;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.cell {
|
|
157
|
+
margin: 0 0 18px;
|
|
158
|
+
animation: rise 0.18s ease-out;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@keyframes rise {
|
|
162
|
+
from { opacity: 0; transform: translateY(3px); }
|
|
163
|
+
to { opacity: 1; transform: none; }
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
@media (prefers-reduced-motion: reduce) {
|
|
167
|
+
.cell { animation: none; }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.line {
|
|
171
|
+
display: flex;
|
|
172
|
+
align-items: flex-start;
|
|
173
|
+
gap: 10px;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.line + .line { margin-top: 3px; }
|
|
177
|
+
|
|
178
|
+
.tag {
|
|
179
|
+
flex: none;
|
|
180
|
+
width: var(--label);
|
|
181
|
+
text-align: right;
|
|
182
|
+
font-family: var(--mono);
|
|
183
|
+
font-size: 11.5px;
|
|
184
|
+
line-height: 1.55;
|
|
185
|
+
color: var(--ink-faint);
|
|
186
|
+
user-select: none;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.line.in .tag { color: var(--accent); }
|
|
190
|
+
|
|
191
|
+
.body {
|
|
192
|
+
flex: 1;
|
|
193
|
+
min-width: 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.line.in .body {
|
|
197
|
+
font-family: var(--mono);
|
|
198
|
+
font-size: 13px;
|
|
199
|
+
white-space: pre-wrap;
|
|
200
|
+
word-break: break-word;
|
|
201
|
+
cursor: text;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.line.in .body:hover { color: var(--accent); }
|
|
205
|
+
|
|
206
|
+
pre.text {
|
|
207
|
+
margin: 0;
|
|
208
|
+
font-family: var(--mono);
|
|
209
|
+
font-size: 13px;
|
|
210
|
+
white-space: pre;
|
|
211
|
+
overflow-x: auto;
|
|
212
|
+
color: var(--result);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
pre.stdout {
|
|
216
|
+
margin: 0 0 4px;
|
|
217
|
+
font-family: var(--mono);
|
|
218
|
+
font-size: 13px;
|
|
219
|
+
white-space: pre;
|
|
220
|
+
overflow-x: auto;
|
|
221
|
+
color: var(--ink-dim);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.tex {
|
|
225
|
+
margin-top: 6px;
|
|
226
|
+
overflow-x: auto;
|
|
227
|
+
overflow-y: hidden;
|
|
228
|
+
padding: 2px 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.tex .katex { font-size: 1.12em; }
|
|
232
|
+
/* A worksheet reads down the left edge; display maths must not centre. */
|
|
233
|
+
.tex .katex-display { margin: 0; text-align: left; }
|
|
234
|
+
.tex .katex-display > .katex { text-align: left; }
|
|
235
|
+
|
|
236
|
+
.source {
|
|
237
|
+
display: block;
|
|
238
|
+
margin-top: 6px;
|
|
239
|
+
padding: 6px 9px;
|
|
240
|
+
border-radius: 6px;
|
|
241
|
+
background: var(--sunken);
|
|
242
|
+
font-family: var(--mono);
|
|
243
|
+
font-size: 12px;
|
|
244
|
+
color: var(--ink-dim);
|
|
245
|
+
white-space: pre-wrap;
|
|
246
|
+
word-break: break-all;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.plot { margin-top: 6px; }
|
|
250
|
+
.plot svg { width: 100%; max-width: 560px; height: auto; border-radius: 8px; }
|
|
251
|
+
|
|
252
|
+
.error { color: var(--danger); }
|
|
253
|
+
.hint { margin-top: 3px; color: var(--ink-dim); font-family: var(--mono); font-size: 12px; }
|
|
254
|
+
.hint span { display: block; }
|
|
255
|
+
.hint span:first-child { color: var(--link); }
|
|
256
|
+
.note { color: var(--ink-faint); font-size: 12px; }
|
|
257
|
+
.warning { color: var(--accent); font-family: var(--mono); font-size: 12px; margin-bottom: 3px; }
|
|
258
|
+
.took { color: var(--ink-faint); font-size: 11px; font-family: var(--mono); }
|
|
259
|
+
|
|
260
|
+
/* ---- tables: /help, /vars, /assumptions ---------------------------------- */
|
|
261
|
+
|
|
262
|
+
table.rows {
|
|
263
|
+
width: 100%;
|
|
264
|
+
border-collapse: collapse;
|
|
265
|
+
font-size: 13px;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
table.rows td {
|
|
269
|
+
padding: 3px 12px 3px 0;
|
|
270
|
+
vertical-align: baseline;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
table.rows td.name {
|
|
274
|
+
width: 1%;
|
|
275
|
+
white-space: nowrap;
|
|
276
|
+
font-family: var(--mono);
|
|
277
|
+
font-size: 12.5px;
|
|
278
|
+
color: var(--accent);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
table.rows td.value { font-family: var(--mono); font-size: 12.5px; }
|
|
282
|
+
|
|
283
|
+
.doc h2 {
|
|
284
|
+
margin: 0 0 4px;
|
|
285
|
+
font-family: var(--mono);
|
|
286
|
+
font-size: 13px;
|
|
287
|
+
font-weight: 600;
|
|
288
|
+
color: var(--accent);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.doc p { margin: 0 0 6px; max-width: 62ch; }
|
|
292
|
+
.doc .field { margin-bottom: 6px; max-width: 72ch; }
|
|
293
|
+
.doc .field > b { color: var(--link); font-weight: 600; }
|
|
294
|
+
.doc a { color: var(--link); }
|
|
295
|
+
.doc ul { margin: 0; padding-left: 18px; }
|
|
296
|
+
|
|
297
|
+
/* ---- greeting ----------------------------------------------------------- */
|
|
298
|
+
|
|
299
|
+
.greeting { max-width: 64ch; color: var(--ink-dim); }
|
|
300
|
+
.greeting p { margin: 0 0 10px; }
|
|
301
|
+
.greeting .dim { color: var(--ink-faint); font-size: 12.5px; }
|
|
302
|
+
.greeting code { font-family: var(--mono); }
|
|
303
|
+
|
|
304
|
+
.examples {
|
|
305
|
+
list-style: none;
|
|
306
|
+
margin: 0 0 14px;
|
|
307
|
+
padding: 0;
|
|
308
|
+
display: flex;
|
|
309
|
+
flex-wrap: wrap;
|
|
310
|
+
gap: 6px;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.examples button {
|
|
314
|
+
padding: 4px 10px;
|
|
315
|
+
border: 1px solid var(--rule);
|
|
316
|
+
border-radius: 999px;
|
|
317
|
+
background: var(--panel);
|
|
318
|
+
color: var(--ink-dim);
|
|
319
|
+
font-family: var(--mono);
|
|
320
|
+
font-size: 12px;
|
|
321
|
+
cursor: pointer;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.examples button:hover {
|
|
325
|
+
border-color: var(--accent);
|
|
326
|
+
color: var(--accent);
|
|
327
|
+
background: var(--accent-soft);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/* ---- composer ----------------------------------------------------------- */
|
|
331
|
+
|
|
332
|
+
.composer {
|
|
333
|
+
flex: none;
|
|
334
|
+
border-top: 1px solid var(--rule);
|
|
335
|
+
background: var(--panel);
|
|
336
|
+
padding: 10px var(--gutter);
|
|
337
|
+
padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.prompt-row {
|
|
341
|
+
display: flex;
|
|
342
|
+
align-items: baseline;
|
|
343
|
+
gap: 10px;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.prompt {
|
|
347
|
+
flex: none;
|
|
348
|
+
width: var(--label);
|
|
349
|
+
text-align: right;
|
|
350
|
+
font-family: var(--mono);
|
|
351
|
+
font-size: 11.5px;
|
|
352
|
+
color: var(--accent);
|
|
353
|
+
user-select: none;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
#input {
|
|
357
|
+
flex: 1;
|
|
358
|
+
min-width: 0;
|
|
359
|
+
max-height: 9.5em;
|
|
360
|
+
border: 0;
|
|
361
|
+
padding: 0;
|
|
362
|
+
resize: none;
|
|
363
|
+
background: transparent;
|
|
364
|
+
color: var(--ink);
|
|
365
|
+
font-family: var(--mono);
|
|
366
|
+
font-size: 13.5px;
|
|
367
|
+
line-height: 1.5;
|
|
368
|
+
outline: none;
|
|
369
|
+
overflow-y: auto;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
#input::placeholder { color: var(--ink-faint); }
|
|
373
|
+
|
|
374
|
+
.completions {
|
|
375
|
+
display: flex;
|
|
376
|
+
flex-wrap: wrap;
|
|
377
|
+
gap: 4px;
|
|
378
|
+
margin: 0 0 8px calc(var(--label) + 10px);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.completions button {
|
|
382
|
+
padding: 2px 8px;
|
|
383
|
+
border: 1px solid var(--rule);
|
|
384
|
+
border-radius: 5px;
|
|
385
|
+
background: var(--sunken);
|
|
386
|
+
color: var(--ink-dim);
|
|
387
|
+
font-family: var(--mono);
|
|
388
|
+
font-size: 12px;
|
|
389
|
+
cursor: pointer;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.completions button[aria-selected="true"] {
|
|
393
|
+
border-color: var(--accent);
|
|
394
|
+
background: var(--accent-soft);
|
|
395
|
+
color: var(--accent);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/* The window is narrow before it is anything else. */
|
|
399
|
+
@media (max-width: 560px) {
|
|
400
|
+
:root { --gutter: 12px; --label: 3.2rem; }
|
|
401
|
+
.line { gap: 7px; }
|
|
402
|
+
}
|