pinokiod 3.6.30 → 3.6.31

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.
package/server/index.js CHANGED
@@ -28,8 +28,6 @@ const multer = require('multer');
28
28
 
29
29
  const ejs = require('ejs');
30
30
 
31
- const DEFAULT_PORT = 42000
32
-
33
31
  const ex = fn => (req, res, next) => {
34
32
  Promise.resolve(fn(req, res, next)).catch(next);
35
33
  };
@@ -47,8 +45,7 @@ class Server {
47
45
  constructor(config) {
48
46
  this.tabs = {}
49
47
  this.agent = config.agent
50
- this.port = DEFAULT_PORT
51
- // this.port = config.port
48
+ this.port = config.port
52
49
  this.kernel = new Kernel(config.store)
53
50
  // this.tunnels = {}
54
51
  this.version = {
@@ -86,6 +83,30 @@ class Server {
86
83
  exists (s) {
87
84
  return new Promise(r=>fs.access(s, fs.constants.F_OK, e => r(!e)))
88
85
  }
86
+ winBuildNumber() {
87
+ let osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release());
88
+ let buildNumber = 0;
89
+ if (osVersion && osVersion.length === 4) {
90
+ buildNumber = parseInt(osVersion[3]);
91
+ }
92
+ return buildNumber;
93
+ }
94
+ compatible() {
95
+ if (this.kernel.platform === "win32") {
96
+ let buildNumber = this.winBuildNumber()
97
+ if (buildNumber < 18309) {
98
+ // must use conpty for node-pty, and conpty is only supported in win>=18309
99
+ console.log("Windows buildNumber", buildNumber)
100
+ throw new Error(`Pinokio supports Windows release 18309 and up (current system: ${buildNumber}`)
101
+ }
102
+
103
+ // if (buildNumber > 25000) {
104
+ // console.log("Windows buildNumber", buildNumber)
105
+ // throw new Error(`Pinokio does not currently support Windows Canary (versions 25000 and up). The current system is ${buildNumber}`)
106
+ //
107
+ // }
108
+ }
109
+ }
89
110
  getMemory(filepath) {
90
111
  let localMem = this.kernel.memory.local[filepath]
91
112
  let globalMem = this.kernel.memory.global[filepath]
@@ -121,7 +142,7 @@ class Server {
121
142
  return items.map((x) => {
122
143
  let name
123
144
  let description
124
- let icon = "/pinokio-black.png"
145
+ let icon
125
146
  let uri
126
147
  if (meta) {
127
148
  let m = meta[x.name]
@@ -130,8 +151,7 @@ class Server {
130
151
  if (m && m.icon) {
131
152
  icon = m.icon
132
153
  } else {
133
- icon = "/pinokio-black.png"
134
- //icon = null
154
+ icon = null
135
155
  }
136
156
  uri = x.name
137
157
  } else {
@@ -171,60 +191,6 @@ class Server {
171
191
  }
172
192
  })
173
193
  }
174
- async init_shells(dir) {
175
- let current = this.kernel.path(dir, "SHELLS")
176
- let exists = await this.exists(current)
177
- if (exists) {
178
- // SHELLS folder exists, skip
179
- } else {
180
- // if SHELLS doesn't exist, need to create one
181
- // 1. if _SHELLS exists, create SHELLS by copying the contents of _SHELLS into SHELLS
182
- // 2. if _SHELLS doesn't exist, just write SHELLS
183
- // if _ENVIRONMENT exists,
184
- let _shells = this.kernel.path(dir, "_SHELLS")
185
- let _dest_shells = this.kernel.path(dir, "SHELLS")
186
- await fs.promises.mkdir(_dest_shells).catch((e) => { })
187
-
188
- // write _.json
189
- let defaultShell = path.resolve(_dest_shells, "_.json")
190
- let default_shell_exists = await this.exists(_shells)
191
- if (!default_shell_exists) {
192
- await fs.promises.writeFile(defaultShell, JSON.stringify({
193
- run: [{
194
- method: "shell.run",
195
- params: {
196
- message: [
197
- "npm install -g @anthropic-ai/claude-code",
198
- ],
199
- }
200
- }, {
201
- method: "shell.run",
202
- params: {
203
- message: [
204
- "claude"
205
- ],
206
- input: true
207
- }
208
- }]
209
- }, null, 2))
210
- }
211
-
212
- let _exists = await this.exists(_shells)
213
- if (_exists) {
214
- let files = await fs.promises.readdir(_shells)
215
- for(let file of files) {
216
- try {
217
- let src = path.resolve(_shells, file)
218
- let dest = path.resolve(_dest_shells, file)
219
- await fs.promises.cp(src, dest, { errorOnExist: true, force: false });
220
- } catch (err) {
221
- }
222
- }
223
- // } else {
224
- // await fs.cp(_shells, _dest_shells, { recursive: true });
225
- }
226
- }
227
- }
228
194
  async init_env(env_dir_path, options) {
229
195
  let current = this.kernel.path(env_dir_path, "ENVIRONMENT")
230
196
  // if environment.json doesn't exist,
@@ -332,12 +298,6 @@ class Server {
332
298
  }
333
299
  let platform = os.platform()
334
300
 
335
- if (config.icon) {
336
- config.icon = `${rawpath}/${config.icon}?raw=true`
337
- } else {
338
- config.icon = "/pinokio-black.png"
339
- }
340
-
341
301
 
342
302
  // get all memory variable stied to the current repository
343
303
  let api_path = this.kernel.path("api", name)
@@ -360,7 +320,6 @@ class Server {
360
320
 
361
321
  await this.init_env("api/" + name)
362
322
 
363
- await this.init_shells("api/" + name)
364
323
 
365
324
  let mode = "run"
366
325
  if (req.query && req.query.mode) {
@@ -368,34 +327,7 @@ class Server {
368
327
  }
369
328
  const env = await this.kernel.env("api/" + name)
370
329
 
371
- let shell_path = this.kernel.path("api", name, "SHELLS")
372
- let shells = []
373
- try {
374
- shells = await fs.promises.readdir(shell_path)
375
- shells = shells.filter((s) => {
376
- return s.endsWith(".json")
377
- })
378
-
379
- let s = []
380
- for(let shell of shells) {
381
- try {
382
- let p = path.resolve(shell_path, shell)
383
- console.log("3p", p)
384
- let json = (await this.kernel.loader.load(p)).resolved
385
- console.log(">>", { json })
386
- if (json && json.run && Array.isArray(json.run)) {
387
- s.push(`/api/${name}/SHELLS/${shell}`)
388
- }
389
- } catch (e) {
390
- }
391
- }
392
- shells = s
393
- } catch (e) {
394
- }
395
- console.log({ shells })
396
-
397
330
  res.render("app", {
398
- shells,
399
331
  error,
400
332
  env,
401
333
  mode,
@@ -547,8 +479,84 @@ class Server {
547
479
  display: ["form"]
548
480
  })
549
481
  } else if (pathComponents.length === 0 && req.query.mode === "download") {
550
- let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check_bin()
551
- console.log({ requirements, install_required, requirements_pending })
482
+
483
+ let requirements = [{
484
+ name: "conda",
485
+ }, {
486
+ name: "git",
487
+ }, {
488
+ name: "zip",
489
+ }, {
490
+ name: "node",
491
+ }, {
492
+ name: "ffmpeg",
493
+ // type: "conda",
494
+ // name: "ffmpeg",
495
+ // args: "-c conda-forge"
496
+ }]
497
+ let platform = os.platform()
498
+ if (platform === "win32") {
499
+ requirements.push({
500
+ name: "registry"
501
+ })
502
+ requirements.push({
503
+ name: "vs"
504
+ })
505
+ }
506
+ if (platform === "darwin") {
507
+ requirements.push({
508
+ name: "brew"
509
+ })
510
+ }
511
+ if (platform === "linux") {
512
+ requirements.push({
513
+ name: "gxx"
514
+ })
515
+ }
516
+ if (this.kernel.gpu === "nvidia") {
517
+ requirements.push({
518
+ name: "cuda",
519
+ })
520
+ }
521
+ requirements = requirements.concat([{
522
+ name: "py"
523
+ }, {
524
+ name: "cloudflared"
525
+ }, {
526
+ name: "playwright"
527
+ }, {
528
+ name: "huggingface"
529
+ }, {
530
+ name: "uv"
531
+ }])
532
+
533
+ let requirements_pending = !this.kernel.bin.installed_initialized
534
+
535
+
536
+ let install_required = true
537
+ if (!requirements_pending) {
538
+ install_required = false
539
+ for(let i=0; i<requirements.length; i++) {
540
+ let r = requirements[i]
541
+ console.time(r.name)
542
+ let installed = await this.installed(r)
543
+ console.log({ r, installed })
544
+ console.timeEnd(r.name)
545
+ requirements[i].installed = installed
546
+ if (!installed) {
547
+ install_required = true
548
+ }
549
+ }
550
+ }
551
+
552
+ let error = null
553
+ try {
554
+ this.compatible()
555
+ } catch (e) {
556
+ error = e.message
557
+ install_required = true
558
+ }
559
+
552
560
 
553
561
  res.render("download", {
554
562
  error,
@@ -908,7 +916,7 @@ class Server {
908
916
 
909
917
  let error = null
910
918
  try {
911
- this.kernel.bin.compatible()
919
+ this.compatible()
912
920
  } catch (e) {
913
921
  error = e.message
914
922
  install_required = true
@@ -1270,7 +1278,7 @@ class Server {
1270
1278
  //let name = (x.name.startsWith("0x") ? Buffer.from(x.name.slice(2), "hex").toString() : x.name)
1271
1279
  let name
1272
1280
  let description
1273
- let icon = "/pinokio-black.png"
1281
+ let icon
1274
1282
  let uri
1275
1283
  if (meta) {
1276
1284
  let m = meta[x.name]
@@ -1279,8 +1287,7 @@ class Server {
1279
1287
  if (m && m.icon) {
1280
1288
  icon = m.icon
1281
1289
  } else {
1282
- //icon = null
1283
- icon = "/pinokio-black.png"
1290
+ icon = null
1284
1291
  }
1285
1292
  uri = x.name
1286
1293
  } else {
@@ -1879,76 +1886,76 @@ class Server {
1879
1886
  // 5. existing home is NOT set + new home is set => go through with the "home" setting procedure
1880
1887
  // 6. existing home is NOT set + new home is NOT set => don't touch anything => the homedir will be the default home
1881
1888
 
1882
- // // 2. HOME
1883
- // // 2.1. Check if the config includes NEW_HOME => if so,
1884
- // // - move the HOME folder to NEW_HOME
1885
- // // - set HOME=NEW_HOME
1886
- // // - remove NEW_HOME
1887
- // let existing_home = this.kernel.store.get("home")
1888
- // let new_home = this.kernel.store.get("new_home")
1889
- //
1890
- // if (existing_home) {
1891
- // let exists = await fse.pathExists(existing_home)
1892
- // if (exists) {
1893
- // if (new_home) {
1894
- // let new_home_exists = await fse.pathExists(new_home)
1895
- // if (new_home_exists) {
1896
- // // - existing home is set
1897
- // // - existing home exists
1898
- // // - new home is set
1899
- // // - new home exists already
1900
- // // => delete store.new_home ==> will load at store.home
1901
- // this.kernel.store.delete("new_home")
1902
- // } else {
1903
- // // - existing home is set
1904
- // // - existing home exists
1905
- // // - new home is set
1906
- // // - new home does not exist
1907
- // // => run mv()
1908
- // // => update store.home
1909
- // // => delete store.new_home
1910
- // await this.mv(existing_home, new_home)
1911
- // this.kernel.store.set("home", new_home)
1912
- // this.kernel.store.delete("new_home")
1913
- // }
1914
- // } else {
1915
- // // - existing home is set
1916
- // // - existing home exists
1917
- // // - new home is not set
1918
- // // => This is most typical scenario => don't touch anything => the homedir will be the existing home
1919
- // }
1920
- // } else {
1921
- // if (new_home) {
1922
- // // - existing home is set
1923
- // // - but the existing home path DOES NOT exist
1924
- // // - new home is set
1925
- // // => This is an invalid scenario => Just to avoid disaster, just delete store.home and delete store.new_home
1926
- // // => the app will load at ~/pinokio
1927
- // this.kernel.store.delete("home")
1928
- // this.kernel.store.delete("new_home")
1929
- // } else {
1930
- // // - existing home is set
1931
- // // - but the existing home path DOES NOT exist
1932
- // // - new home is NOT set
1933
- // // => This is an invalid scenario => just delete store.home
1934
- // // => the app will load at ~/pinokio
1935
- // this.kernel.store.delete("home")
1936
- // }
1937
- // }
1938
- // } else {
1939
- // if (new_home) {
1940
- // // - existing home is NOT set
1941
- // // - new home is set
1942
- // // => update store.home
1943
- // // => delete store.new_home
1944
- // this.kernel.store.set("home", new_home)
1945
- // this.kernel.store.delete("new_home")
1946
- // } else {
1947
- // // - existing home is NOT set
1948
- // // - new home is NOT set
1949
- // // => don't touch anything => will load at ~/pinokio
1950
- // }
1951
- // }
1889
+ // 2. HOME
1890
+ // 2.1. Check if the config includes NEW_HOME => if so,
1891
+ // - move the HOME folder to NEW_HOME
1892
+ // - set HOME=NEW_HOME
1893
+ // - remove NEW_HOME
1894
+ let existing_home = this.kernel.store.get("home")
1895
+ let new_home = this.kernel.store.get("new_home")
1896
+
1897
+ if (existing_home) {
1898
+ let exists = await fse.pathExists(existing_home)
1899
+ if (exists) {
1900
+ if (new_home) {
1901
+ let new_home_exists = await fse.pathExists(new_home)
1902
+ if (new_home_exists) {
1903
+ // - existing home is set
1904
+ // - existing home exists
1905
+ // - new home is set
1906
+ // - new home exists already
1907
+ // => delete store.new_home ==> will load at store.home
1908
+ this.kernel.store.delete("new_home")
1909
+ } else {
1910
+ // - existing home is set
1911
+ // - existing home exists
1912
+ // - new home is set
1913
+ // - new home does not exist
1914
+ // => run mv()
1915
+ // => update store.home
1916
+ // => delete store.new_home
1917
+ await this.mv(existing_home, new_home)
1918
+ this.kernel.store.set("home", new_home)
1919
+ this.kernel.store.delete("new_home")
1920
+ }
1921
+ } else {
1922
+ // - existing home is set
1923
+ // - existing home exists
1924
+ // - new home is not set
1925
+ // => This is most typical scenario => don't touch anything => the homedir will be the existing home
1926
+ }
1927
+ } else {
1928
+ if (new_home) {
1929
+ // - existing home is set
1930
+ // - but the existing home path DOES NOT exist
1931
+ // - new home is set
1932
+ // => This is an invalid scenario => Just to avoid disaster, just delete store.home and delete store.new_home
1933
+ // => the app will load at ~/pinokio
1934
+ this.kernel.store.delete("home")
1935
+ this.kernel.store.delete("new_home")
1936
+ } else {
1937
+ // - existing home is set
1938
+ // - but the existing home path DOES NOT exist
1939
+ // - new home is NOT set
1940
+ // => This is an invalid scenario => just delete store.home
1941
+ // => the app will load at ~/pinokio
1942
+ this.kernel.store.delete("home")
1943
+ }
1944
+ }
1945
+ } else {
1946
+ if (new_home) {
1947
+ // - existing home is NOT set
1948
+ // - new home is set
1949
+ // => update store.home
1950
+ // => delete store.new_home
1951
+ this.kernel.store.set("home", new_home)
1952
+ this.kernel.store.delete("new_home")
1953
+ } else {
1954
+ // - existing home is NOT set
1955
+ // - new home is NOT set
1956
+ // => don't touch anything => will load at ~/pinokio
1957
+ }
1958
+ }
1952
1959
  }
1953
1960
  async setConfig(config) {
1954
1961
  let home = this.kernel.store.get("home")
@@ -1973,14 +1980,13 @@ class Server {
1973
1980
  throw new Error("Invalid path: " + config.home)
1974
1981
  }
1975
1982
 
1976
- // // check if the destination already exists => throw error
1977
- // let exists = await fse.pathExists(config.home)
1978
- // if (exists) {
1979
- // throw new Error(`The path ${config.home} already exists. Please remove the folder and retry`)
1980
- // }
1983
+ // check if the destination already exists => throw error
1984
+ let exists = await fse.pathExists(config.home)
1985
+ if (exists) {
1986
+ throw new Error(`The path ${config.home} already exists. Please remove the folder and retry`)
1987
+ }
1981
1988
 
1982
- //this.kernel.store.set("new_home", config.home)
1983
- this.kernel.store.set("home", config.home)
1989
+ this.kernel.store.set("new_home", config.home)
1984
1990
  }
1985
1991
 
1986
1992
  }
@@ -2009,6 +2015,7 @@ class Server {
2009
2015
 
2010
2016
  home = this.kernel.store.get("home")
2011
2017
  theme = this.kernel.store.get("theme")
2018
+ let new_home = this.kernel.store.get("new_home")
2012
2019
  }
2013
2020
  async startLogging(homedir) {
2014
2021
  console.log(">>>>>>> startLogging", homedir)
@@ -2045,27 +2052,7 @@ class Server {
2045
2052
  }
2046
2053
  }
2047
2054
  }
2048
- async running(port) {
2049
- let p = port || DEFAULT_PORT
2050
- const available = await Util.is_port_available(p)
2051
- if (available) {
2052
- return false
2053
- } else {
2054
- return true
2055
- }
2056
- }
2057
2055
  async start(options) {
2058
- console.time("###### ALL")
2059
-
2060
- // // ONLY start if not already running
2061
- // let is_running = await this.running(this.port || DEFAULT_PORT)
2062
- // if (is_running) {
2063
- // console.log("pinokiod already running, skip..")
2064
- // return
2065
- // }
2066
- //
2067
-
2068
- console.time("######### 1")
2069
2056
  this.debug = false
2070
2057
  if (options) {
2071
2058
  this.debug = options.debug
@@ -2097,14 +2084,10 @@ class Server {
2097
2084
  // } catch (e) {
2098
2085
  // }
2099
2086
  }
2100
- console.timeEnd("######### 1")
2101
2087
 
2102
2088
  // configure from kernel.store
2103
- console.time("###### 2 syncConfig")
2104
2089
  await this.syncConfig()
2105
- console.timeEnd("###### 2 syncConfig")
2106
2090
 
2107
- console.time("###### 3 startLogging")
2108
2091
  try {
2109
2092
  let _home = this.kernel.store.get("home")
2110
2093
  console.log({ _home })
@@ -2114,12 +2097,11 @@ class Server {
2114
2097
  } catch (e) {
2115
2098
  console.log("start logging attempt", e)
2116
2099
  }
2117
- console.timeEnd("###### 3 startLogging")
2118
2100
 
2119
2101
  // determine port if port is not passed in
2120
2102
 
2121
2103
  if (!this.port) {
2122
- this.port = DEFAULT_PORT
2104
+ this.port = 42000
2123
2105
  // let platform = os.platform()
2124
2106
  // if (platform === 'linux') {
2125
2107
  // // on linux you are not allowed to listen on ports below 1024
@@ -2169,19 +2151,15 @@ class Server {
2169
2151
  console.log("[DONE] Updating to the new version")
2170
2152
  }
2171
2153
  }
2172
- console.time("###### 4 kernel.init")
2154
+
2173
2155
  // initialize kernel
2174
2156
  await this.kernel.init({ port: this.port})
2175
- console.timeEnd("###### 4 kernel.init")
2176
2157
 
2177
2158
  console.log({ needInitHome })
2178
- console.time("###### 5 initHome")
2179
2159
  if (needInitHome) {
2180
2160
  await this.kernel.initHome()
2181
2161
  }
2182
- console.timeEnd("###### 5 initHome")
2183
2162
 
2184
- console.time("###### 6")
2185
2163
  if (this.kernel.homedir) {
2186
2164
  let ex = await this.kernel.exists(this.kernel.homedir, "ENVIRONMENT")
2187
2165
  if (!ex) {
@@ -2189,7 +2167,6 @@ class Server {
2189
2167
  await fs.promises.writeFile(path.resolve(this.kernel.homedir, "ENVIRONMENT"), str)
2190
2168
  }
2191
2169
  }
2192
- console.timeEnd("###### 6")
2193
2170
 
2194
2171
 
2195
2172
 
@@ -2274,7 +2251,6 @@ class Server {
2274
2251
 
2275
2252
 
2276
2253
  //await this.configure()
2277
- console.time("###### 7")
2278
2254
 
2279
2255
  this.started = false
2280
2256
  this.app = express();
@@ -2521,20 +2497,6 @@ class Server {
2521
2497
  let str = await fs.promises.readFile(req.query.logpath, "utf8")
2522
2498
  res.send(str)
2523
2499
  }))
2524
- this.app.post("/mkdir", ex(async (req, res) => {
2525
- let folder = req.body.folder
2526
- let folder_path = path.resolve(this.kernel.api.userdir, req.body.folder)
2527
- try {
2528
- await fs.promises.mkdir(folder_path)
2529
- res.json({
2530
- success: "/pinokio/browser/"+folder
2531
- })
2532
- } catch (e) {
2533
- res.json({
2534
- error: e.message
2535
- })
2536
- }
2537
- }))
2538
2500
  this.app.post("/proxy", ex(async (req, res) => {
2539
2501
  /*
2540
2502
  req.body := {
@@ -2672,8 +2634,6 @@ class Server {
2672
2634
  }
2673
2635
  if (config.icon) {
2674
2636
  config.icon = `/${env_path}/${config.icon}?raw=true`
2675
- } else {
2676
- config.icon = "/pinokio-black.png"
2677
2637
  }
2678
2638
 
2679
2639
  let name
@@ -2734,8 +2694,6 @@ class Server {
2734
2694
  config.pre.forEach((item) => {
2735
2695
  if (item.icon) {
2736
2696
  item.icon = `/api/${req.params.name}/${item.icon}?raw=true`
2737
- } else {
2738
- item.icon = "/pinokio-black.png"
2739
2697
  }
2740
2698
  if (!item.href.startsWith("http")) {
2741
2699
  item.href = path.resolve(this.kernel.homedir, "api", req.params.name, item.href)
@@ -2844,43 +2802,6 @@ class Server {
2844
2802
  }
2845
2803
  res.json({ config: this.xterm })
2846
2804
  }))
2847
- // this.app.get("/shell", ex(async (req, res) => {
2848
- // console.log("req.query", req.query)
2849
- // res.render("terminal", {
2850
- // memory: [],
2851
- // logo: this.logo,
2852
- // theme: this.theme,
2853
- // run: true,
2854
- // stop: false,
2855
- // runnable: true,
2856
- // agent: this.agent,
2857
- //// uri,
2858
- // mod: true,
2859
- // json: true,
2860
- // js: false,
2861
- // install_required: false,
2862
- // current: req.originalUrl,
2863
- // })
2864
- // }))
2865
- this.app.get("/shells/:name", ex(async (req, res) => {
2866
- let p = this.kernel.path("api", req.params.name)
2867
- let venv_folders = await Util.find_venv(p)
2868
- console.log({ venv_folders })
2869
- let items = venv_folders.map((f) => {
2870
- let args = {
2871
- message: "",
2872
- venv: f
2873
- }
2874
- return {
2875
- name: f,
2876
- cmd: new URLSearchParams(args).toString()
2877
- }
2878
- })
2879
- console.log({ items })
2880
- res.json({
2881
- items
2882
- })
2883
- }))
2884
2805
  this.app.get("/du/:name", ex(async (req, res) => {
2885
2806
  let p = this.kernel.path("api", req.params.name)
2886
2807
  let d1 = await Util.du(p)
@@ -3500,8 +3421,6 @@ class Server {
3500
3421
  // install
3501
3422
  this.server = httpserver.createServer(this.app);
3502
3423
  this.socket = new Socket(this)
3503
- console.timeEnd("###### 7")
3504
- console.time("###### 8")
3505
3424
  await new Promise((resolve, reject) => {
3506
3425
  this.listening = this.server.listen(this.port, () => {
3507
3426
  console.log(`Server listening on port ${this.port}`)
@@ -3511,8 +3430,6 @@ class Server {
3511
3430
  server: this.listening
3512
3431
  });
3513
3432
  })
3514
- console.timeEnd("###### 8")
3515
- console.timeEnd("###### ALL")
3516
3433
 
3517
3434
  }
3518
3435
  }
Binary file
@@ -1701,11 +1701,6 @@ footer.status {
1701
1701
  text-transform: uppercase;
1702
1702
  }
1703
1703
 
1704
- #fsmenu {
1705
- background: rgba(0,0,100,0.04);
1706
- display: flex;
1707
- padding: 20px 20px 0;
1708
- }
1709
1704
  mark{
1710
1705
  background: firebrick;
1711
1706
  color: white;