@phenx-inc/ctlsurf 0.12.0 → 0.13.1

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.
Files changed (32) hide show
  1. package/out/headless/index.mjs +29 -19
  2. package/out/headless/index.mjs.map +2 -2
  3. package/out/main/index.js +35 -19
  4. package/out/renderer/assets/{cssMode-R8fTFLae.js → cssMode-KhYyY-BM.js} +3 -3
  5. package/out/renderer/assets/{freemarker2-CyjWzWoP.js → freemarker2-BFsSgcWk.js} +1 -1
  6. package/out/renderer/assets/{handlebars-CrhCGIRM.js → handlebars-Be6_aBsm.js} +1 -1
  7. package/out/renderer/assets/{html-Cc3dPNZr.js → html-G8mvsyfc.js} +1 -1
  8. package/out/renderer/assets/{htmlMode-BWtG6ywk.js → htmlMode-Cfd5VCjw.js} +3 -3
  9. package/out/renderer/assets/{index-BL9njXIL.css → index-Bi_MjBqs.css} +233 -8
  10. package/out/renderer/assets/{index-kkG56aKv.js → index-MqgBcJzy.js} +392 -120
  11. package/out/renderer/assets/{javascript-C8T_SdLC.js → javascript-XyDWi6dC.js} +2 -2
  12. package/out/renderer/assets/{jsonMode-D1zR4VVU.js → jsonMode-DZT5gBVW.js} +3 -3
  13. package/out/renderer/assets/{liquid-COSMe9Uf.js → liquid-BuajCNJ9.js} +1 -1
  14. package/out/renderer/assets/{lspLanguageFeatures-CXGfrtzl.js → lspLanguageFeatures-BYkg2Yc-.js} +1 -1
  15. package/out/renderer/assets/{mdx-RfS44T-L.js → mdx-D0Qx2GMS.js} +1 -1
  16. package/out/renderer/assets/{python-DFSWKHUu.js → python-ChncNy6S.js} +1 -1
  17. package/out/renderer/assets/{razor-DtH2_uvo.js → razor-C6MUtfaS.js} +1 -1
  18. package/out/renderer/assets/{tsMode-BYIQ-S9Z.js → tsMode-C2aMdzdU.js} +1 -1
  19. package/out/renderer/assets/{typescript-HhQUr1Rb.js → typescript-CB71AtAT.js} +1 -1
  20. package/out/renderer/assets/{xml-KuhgdKfD.js → xml-tHS1Ec9w.js} +1 -1
  21. package/out/renderer/assets/{yaml-W_kzCFH7.js → yaml-CZcp5ueF.js} +1 -1
  22. package/out/renderer/index.html +2 -2
  23. package/package.json +1 -1
  24. package/src/main/index.ts +13 -1
  25. package/src/main/orchestrator.ts +8 -1
  26. package/src/main/ticketStore.ts +26 -20
  27. package/src/renderer/App.tsx +19 -3
  28. package/src/renderer/components/CtlsurfPanel.tsx +169 -35
  29. package/src/renderer/components/SpeakControls.tsx +8 -2
  30. package/src/renderer/components/TerminalPanel.tsx +141 -44
  31. package/src/renderer/components/TicketPanel.tsx +126 -29
  32. package/src/renderer/styles.css +233 -8
package/out/main/index.js CHANGED
@@ -11276,7 +11276,10 @@ function findPageByTitle(pages, title) {
11276
11276
  }
11277
11277
  class TicketStore {
11278
11278
  api;
11279
- blockCache = /* @__PURE__ */ new Map();
11279
+ // Per-cwd cache of the resolved Tickets datastore: the block (the datastore
11280
+ // element) and the Agent Datastore page it lives on. Both ids are handed to
11281
+ // the coding agent so it can read/update tickets via the ctlsurf MCP tools.
11282
+ dsCache = /* @__PURE__ */ new Map();
11280
11283
  constructor(api) {
11281
11284
  this.api = api;
11282
11285
  }
@@ -11285,11 +11288,11 @@ class TicketStore {
11285
11288
  const title = input.title?.trim();
11286
11289
  if (!title) return { ok: false, error: "Title is required" };
11287
11290
  try {
11288
- const blockId = await this.ensureDatastore(cwd, true);
11289
- if (!blockId) {
11291
+ const ds = await this.ensureDatastore(cwd, true);
11292
+ if (!ds) {
11290
11293
  return { ok: false, error: "No ctlsurf project found for this folder" };
11291
11294
  }
11292
- await this.api.addRow(blockId, {
11295
+ await this.api.addRow(ds.blockId, {
11293
11296
  Title: title,
11294
11297
  Description: input.description?.trim() || "",
11295
11298
  Status: input.status || "Open",
@@ -11308,9 +11311,9 @@ class TicketStore {
11308
11311
  const title = input.title?.trim();
11309
11312
  if (!title) return { ok: false, error: "Title is required" };
11310
11313
  try {
11311
- const blockId = await this.ensureDatastore(cwd, false);
11312
- if (!blockId) return { ok: false, error: "No Tickets datastore for this project" };
11313
- await this.api.updateRow(blockId, rowId, {
11314
+ const ds = await this.ensureDatastore(cwd, false);
11315
+ if (!ds) return { ok: false, error: "No Tickets datastore for this project" };
11316
+ await this.api.updateRow(ds.blockId, rowId, {
11314
11317
  Title: title,
11315
11318
  Description: input.description?.trim() || "",
11316
11319
  Status: input.status || "Open",
@@ -11327,9 +11330,9 @@ class TicketStore {
11327
11330
  * datastore — an unconfigured project simply has no tickets yet. */
11328
11331
  async listTickets(cwd) {
11329
11332
  try {
11330
- const blockId = await this.ensureDatastore(cwd, false);
11331
- if (!blockId) return { ok: true, tickets: [] };
11332
- const res = await this.api.queryRows(blockId, { orderBy: "Created", order: "desc", limit: 200 });
11333
+ const ds = await this.ensureDatastore(cwd, false);
11334
+ if (!ds) return { ok: true, tickets: [] };
11335
+ const res = await this.api.queryRows(ds.blockId, { orderBy: "Created", order: "desc", limit: 200 });
11333
11336
  const tickets = (res?.rows || []).map((r) => ({
11334
11337
  id: r.id,
11335
11338
  title: String(r.data?.Title ?? ""),
@@ -11338,14 +11341,14 @@ class TicketStore {
11338
11341
  priority: String(r.data?.Priority ?? "Med"),
11339
11342
  created: r.data?.Created ?? r.created_at ?? null
11340
11343
  }));
11341
- return { ok: true, tickets };
11344
+ return { ok: true, tickets, blockId: ds.blockId, pageId: ds.pageId };
11342
11345
  } catch (err) {
11343
11346
  log$1(`listTickets failed for ${cwd}: ${err?.message || err}`);
11344
11347
  return { ok: false, tickets: [], error: err?.message || String(err) };
11345
11348
  }
11346
11349
  }
11347
11350
  async ensureDatastore(cwd, create) {
11348
- const cached = this.blockCache.get(cwd);
11351
+ const cached = this.dsCache.get(cwd);
11349
11352
  if (cached) return cached;
11350
11353
  let folder = null;
11351
11354
  try {
@@ -11357,11 +11360,13 @@ class TicketStore {
11357
11360
  const folderDetail = await this.api.getFolder(folder.id);
11358
11361
  const agentPage = findPageByTitle(folderDetail?.pages || [], AGENT_DATASTORE_PAGE_TITLE);
11359
11362
  if (!agentPage?.id) return null;
11360
- const blockId = await this.findOrAdoptBlock(agentPage.id);
11363
+ const pageId = agentPage.id;
11364
+ const blockId = await this.findOrAdoptBlock(pageId);
11361
11365
  if (blockId) {
11362
11366
  await this.ensureColumns(blockId);
11363
- this.blockCache.set(cwd, blockId);
11364
- return blockId;
11367
+ const ds = { blockId, pageId };
11368
+ this.dsCache.set(cwd, ds);
11369
+ return ds;
11365
11370
  }
11366
11371
  if (!create) return null;
11367
11372
  const columns = COLUMNS.map((c, i) => ({
@@ -11370,15 +11375,16 @@ class TicketStore {
11370
11375
  type: c.type,
11371
11376
  ...c.options ? { options: c.options } : {}
11372
11377
  }));
11373
- const created = await this.api.createBlock(agentPage.id, {
11378
+ const created = await this.api.createBlock(pageId, {
11374
11379
  type: "datastore",
11375
11380
  title: DATASTORE_TITLE,
11376
11381
  props: { columns, system_key: SYSTEM_KEY }
11377
11382
  });
11378
11383
  if (created?.id) {
11379
11384
  log$1(`Created "${DATASTORE_TITLE}" datastore on Agent Datastore page for ${cwd}`);
11380
- this.blockCache.set(cwd, created.id);
11381
- return created.id;
11385
+ const ds = { blockId: created.id, pageId };
11386
+ this.dsCache.set(cwd, ds);
11387
+ return ds;
11382
11388
  }
11383
11389
  return null;
11384
11390
  }
@@ -11924,12 +11930,16 @@ class Orchestrator {
11924
11930
  }
11925
11931
  }
11926
11932
  setActiveTab(tabId) {
11933
+ const prevProjectPath = this.getActiveTabProjectPath();
11927
11934
  this.activeTabId = tabId;
11928
11935
  const tab = this.tabs.get(tabId);
11929
11936
  if (tab) {
11930
11937
  this.currentAgent = tab.agent;
11931
11938
  this.currentCwd = tab.cwd;
11932
11939
  }
11940
+ if (this.getActiveTabProjectPath() !== prevProjectPath) {
11941
+ this.events.onCwdChanged();
11942
+ }
11933
11943
  }
11934
11944
  getTabIds() {
11935
11945
  return [...this.tabs.keys()];
@@ -12270,7 +12280,7 @@ const orchestrator = new Orchestrator(
12270
12280
  },
12271
12281
  onCwdChanged: () => {
12272
12282
  mainWindow?.webContents.send("app:cwdChanged");
12273
- updateProjectBadge(orchestrator.cwd);
12283
+ updateProjectBadge(orchestrator.getActiveTabProjectPath() || orchestrator.cwd);
12274
12284
  }
12275
12285
  }
12276
12286
  );
@@ -12293,6 +12303,12 @@ function createWindow() {
12293
12303
  mainWindow.webContents.session.setPermissionRequestHandler((_wc, _permission, callback) => {
12294
12304
  callback(true);
12295
12305
  });
12306
+ mainWindow.webContents.on("did-attach-webview", (_e, contents) => {
12307
+ contents.setWindowOpenHandler(({ url }) => {
12308
+ setImmediate(() => contents.loadURL(url));
12309
+ return { action: "deny" };
12310
+ });
12311
+ });
12296
12312
  if (process.env.ELECTRON_RENDERER_URL) {
12297
12313
  mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL);
12298
12314
  } else {
@@ -1,6 +1,6 @@
1
- import { c as createWebWorker, l as languages } from "./index-kkG56aKv.js";
2
- import { C as CompletionAdapter, H as HoverAdapter, D as DocumentHighlightAdapter, a as DefinitionAdapter, R as ReferenceAdapter, b as DocumentSymbolAdapter, c as RenameAdapter, d as DocumentColorAdapter, F as FoldingRangeAdapter, e as DiagnosticsAdapter, S as SelectionRangeAdapter, f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider } from "./lspLanguageFeatures-CXGfrtzl.js";
3
- import { h, i, j, t, k } from "./lspLanguageFeatures-CXGfrtzl.js";
1
+ import { c as createWebWorker, l as languages } from "./index-MqgBcJzy.js";
2
+ import { C as CompletionAdapter, H as HoverAdapter, D as DocumentHighlightAdapter, a as DefinitionAdapter, R as ReferenceAdapter, b as DocumentSymbolAdapter, c as RenameAdapter, d as DocumentColorAdapter, F as FoldingRangeAdapter, e as DiagnosticsAdapter, S as SelectionRangeAdapter, f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider } from "./lspLanguageFeatures-BYkg2Yc-.js";
3
+ import { h, i, j, t, k } from "./lspLanguageFeatures-BYkg2Yc-.js";
4
4
  const STOP_WHEN_IDLE_FOR = 2 * 60 * 1e3;
5
5
  class WorkerManager {
6
6
  constructor(defaults) {
@@ -1,4 +1,4 @@
1
- import { l as languages } from "./index-kkG56aKv.js";
1
+ import { l as languages } from "./index-MqgBcJzy.js";
2
2
  const EMPTY_ELEMENTS = [
3
3
  "assign",
4
4
  "flush",
@@ -1,4 +1,4 @@
1
- import { l as languages } from "./index-kkG56aKv.js";
1
+ import { l as languages } from "./index-MqgBcJzy.js";
2
2
  const EMPTY_ELEMENTS = [
3
3
  "area",
4
4
  "base",
@@ -1,4 +1,4 @@
1
- import { l as languages } from "./index-kkG56aKv.js";
1
+ import { l as languages } from "./index-MqgBcJzy.js";
2
2
  const EMPTY_ELEMENTS = [
3
3
  "area",
4
4
  "base",
@@ -1,6 +1,6 @@
1
- import { c as createWebWorker, l as languages } from "./index-kkG56aKv.js";
2
- import { H as HoverAdapter, D as DocumentHighlightAdapter, h as DocumentLinkAdapter, F as FoldingRangeAdapter, b as DocumentSymbolAdapter, S as SelectionRangeAdapter, c as RenameAdapter, f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider, C as CompletionAdapter } from "./lspLanguageFeatures-CXGfrtzl.js";
3
- import { a, e, d, R, i, j, t, k } from "./lspLanguageFeatures-CXGfrtzl.js";
1
+ import { c as createWebWorker, l as languages } from "./index-MqgBcJzy.js";
2
+ import { H as HoverAdapter, D as DocumentHighlightAdapter, h as DocumentLinkAdapter, F as FoldingRangeAdapter, b as DocumentSymbolAdapter, S as SelectionRangeAdapter, c as RenameAdapter, f as DocumentFormattingEditProvider, g as DocumentRangeFormattingEditProvider, C as CompletionAdapter } from "./lspLanguageFeatures-BYkg2Yc-.js";
3
+ import { a, e, d, R, i, j, t, k } from "./lspLanguageFeatures-BYkg2Yc-.js";
4
4
  const STOP_WHEN_IDLE_FOR = 2 * 60 * 1e3;
5
5
  class WorkerManager {
6
6
  constructor(defaults) {
@@ -7486,6 +7486,17 @@ html, body, #root {
7486
7486
  background: #1f2335;
7487
7487
  }
7488
7488
 
7489
+ .titlebar-btn:disabled {
7490
+ opacity: 0.4;
7491
+ cursor: not-allowed;
7492
+ }
7493
+
7494
+ /* Keep the greyed-out look stable on hover for disabled buttons. */
7495
+ .titlebar-btn:disabled:hover {
7496
+ color: #565f89;
7497
+ border-color: #3b3d57;
7498
+ }
7499
+
7489
7500
  .titlebar-separator {
7490
7501
  width: 1px;
7491
7502
  height: 16px;
@@ -7652,6 +7663,15 @@ html, body, #root {
7652
7663
  }
7653
7664
 
7654
7665
  /* Terminal container */
7666
+ .terminal-wrap {
7667
+ position: relative;
7668
+ flex: 1;
7669
+ min-width: 0;
7670
+ min-height: 0;
7671
+ width: 100%;
7672
+ height: 100%;
7673
+ }
7674
+
7655
7675
  .terminal-container {
7656
7676
  width: 100%;
7657
7677
  height: 100%;
@@ -7663,6 +7683,34 @@ html, body, #root {
7663
7683
  height: 100%;
7664
7684
  }
7665
7685
 
7686
+ /* Floating "jump to latest" pill — shown only while scrolled up. */
7687
+ .term-jump-latest {
7688
+ position: absolute;
7689
+ bottom: 14px;
7690
+ right: 20px;
7691
+ z-index: 5;
7692
+ display: inline-flex;
7693
+ align-items: center;
7694
+ gap: 5px;
7695
+ padding: 5px 12px;
7696
+ border-radius: 14px;
7697
+ background: #2a2b3d;
7698
+ border: 1px solid #7aa2f7;
7699
+ color: #c0caf5;
7700
+ font-family: inherit;
7701
+ font-size: 11.5px;
7702
+ font-weight: 600;
7703
+ cursor: pointer;
7704
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
7705
+ transition: background 0.13s ease, transform 0.06s ease;
7706
+ }
7707
+ .term-jump-latest:hover {
7708
+ background: #343650;
7709
+ }
7710
+ .term-jump-latest:active {
7711
+ transform: translateY(1px);
7712
+ }
7713
+
7666
7714
  .terminal-fullscreen {
7667
7715
  width: 100%;
7668
7716
  height: 100%;
@@ -7867,13 +7915,88 @@ html, body, #root {
7867
7915
  width: 100%;
7868
7916
  height: 100%;
7869
7917
  overflow: hidden;
7918
+ display: flex;
7919
+ flex-direction: column;
7920
+ }
7921
+
7922
+ .ctlsurf-addressbar {
7923
+ flex: 0 0 auto;
7924
+ display: flex;
7925
+ align-items: center;
7926
+ gap: 4px;
7927
+ padding: 5px 6px;
7928
+ background: #1a1b26;
7929
+ border-bottom: 1px solid #2a2b3d;
7870
7930
  }
7871
7931
 
7872
- .ctlsurf-panel webview {
7932
+ .ctlsurf-nav-btn {
7933
+ flex: 0 0 auto;
7934
+ width: 24px;
7935
+ height: 24px;
7936
+ display: flex;
7937
+ align-items: center;
7938
+ justify-content: center;
7939
+ padding: 0;
7940
+ background: transparent;
7941
+ border: none;
7942
+ border-radius: 4px;
7943
+ color: #a9b1d6;
7944
+ font-size: 16px;
7945
+ line-height: 1;
7946
+ cursor: pointer;
7947
+ }
7948
+
7949
+ .ctlsurf-nav-btn:hover:not(:disabled) {
7950
+ background: #2a2b3d;
7951
+ }
7952
+
7953
+ .ctlsurf-nav-btn:disabled {
7954
+ color: #565f89;
7955
+ opacity: 0.5;
7956
+ cursor: default;
7957
+ }
7958
+
7959
+ .ctlsurf-address-form {
7960
+ flex: 1 1 auto;
7961
+ display: flex;
7962
+ min-width: 0;
7963
+ }
7964
+
7965
+ .ctlsurf-address-input {
7966
+ width: 100%;
7967
+ height: 26px;
7968
+ padding: 0 11px;
7969
+ background: #16161e;
7970
+ border: 1px solid #2a2b3d;
7971
+ border-radius: 13px;
7972
+ color: #c0caf5;
7973
+ font-size: 12px;
7974
+ outline: none;
7975
+ }
7976
+
7977
+ .ctlsurf-address-input:focus {
7978
+ border-color: #7aa2f7;
7979
+ }
7980
+
7981
+ .ctlsurf-webview-host {
7982
+ flex: 1 1 auto;
7983
+ position: relative;
7984
+ min-height: 0;
7985
+ }
7986
+
7987
+ .ctlsurf-webview-host webview {
7873
7988
  width: 100%;
7874
7989
  height: 100%;
7875
7990
  }
7876
7991
 
7992
+ .ctlsurf-webview-loading {
7993
+ width: 100%;
7994
+ height: 100%;
7995
+ display: flex;
7996
+ align-items: center;
7997
+ justify-content: center;
7998
+ }
7999
+
7877
8000
  /* Status bar */
7878
8001
  .statusbar {
7879
8002
  height: 24px;
@@ -9098,6 +9221,32 @@ html, body, #root {
9098
9221
  color: #7aa2f7;
9099
9222
  font-weight: 600;
9100
9223
  }
9224
+ /* "+ New" action in the header (replaces the old footer button). */
9225
+ .ticket-drawer-new {
9226
+ flex-shrink: 0;
9227
+ align-self: center;
9228
+ display: inline-flex;
9229
+ align-items: center;
9230
+ gap: 3px;
9231
+ padding: 5px 11px;
9232
+ border-radius: 7px;
9233
+ background: rgba(122, 162, 247, 0.14);
9234
+ border: 1px solid rgba(122, 162, 247, 0.3);
9235
+ color: #9db4f5;
9236
+ font-family: inherit;
9237
+ font-size: 12px;
9238
+ font-weight: 600;
9239
+ cursor: pointer;
9240
+ transition: background 0.13s ease, border-color 0.13s ease, color 0.13s ease, transform 0.06s ease;
9241
+ }
9242
+ .ticket-drawer-new:hover {
9243
+ background: rgba(122, 162, 247, 0.22);
9244
+ border-color: rgba(122, 162, 247, 0.5);
9245
+ color: #c0d0fb;
9246
+ }
9247
+ .ticket-drawer-new:active {
9248
+ transform: translateY(1px);
9249
+ }
9101
9250
  .ticket-drawer-warn {
9102
9251
  color: #e0af68;
9103
9252
  }
@@ -9295,6 +9444,49 @@ html, body, #root {
9295
9444
  text-align: center;
9296
9445
  }
9297
9446
 
9447
+ /* ── collapsible "Done" section header ── */
9448
+ .ticket-done-toggle {
9449
+ display: flex;
9450
+ align-items: center;
9451
+ gap: 7px;
9452
+ width: 100%;
9453
+ margin-top: 3px;
9454
+ padding: 7px 4px;
9455
+ background: none;
9456
+ border: none;
9457
+ border-top: 1px solid #262a3f;
9458
+ color: #6b739b;
9459
+ font-family: inherit;
9460
+ font-size: 11.5px;
9461
+ font-weight: 600;
9462
+ letter-spacing: 0.03em;
9463
+ text-transform: uppercase;
9464
+ cursor: pointer;
9465
+ transition: color 0.13s ease;
9466
+ }
9467
+ .ticket-done-toggle:hover {
9468
+ color: #a9b1d6;
9469
+ }
9470
+ .ticket-done-caret {
9471
+ display: inline-block;
9472
+ font-size: 10px;
9473
+ transition: transform 0.13s ease;
9474
+ }
9475
+ .ticket-done-caret.open {
9476
+ transform: rotate(90deg);
9477
+ }
9478
+ .ticket-done-count {
9479
+ margin-left: auto;
9480
+ min-width: 18px;
9481
+ padding: 1px 6px;
9482
+ border-radius: 9px;
9483
+ background: #262a3f;
9484
+ color: #8088ac;
9485
+ font-size: 11px;
9486
+ font-weight: 600;
9487
+ text-align: center;
9488
+ }
9489
+
9298
9490
  .ticket-empty {
9299
9491
  display: flex;
9300
9492
  flex-direction: column;
@@ -9321,24 +9513,57 @@ html, body, #root {
9321
9513
  .ticket-card {
9322
9514
  display: flex;
9323
9515
  align-items: stretch;
9324
- gap: 10px;
9325
9516
  background: #15161f;
9326
9517
  border: 1px solid #262a3f;
9327
9518
  border-radius: 10px;
9328
- padding: 10px 11px 10px 0;
9329
- cursor: pointer;
9330
- text-align: left;
9331
- font-family: inherit;
9332
9519
  overflow: hidden;
9333
- transition: border-color 0.13s ease, background 0.13s ease, transform 0.06s ease;
9520
+ transition: border-color 0.13s ease, background 0.13s ease;
9334
9521
  }
9335
9522
  .ticket-card:hover {
9336
9523
  border-color: #3d425f;
9337
9524
  background: #181a26;
9338
9525
  }
9339
- .ticket-card:active {
9526
+ /* Clickable body of the card — opens the editor. */
9527
+ .ticket-card-open {
9528
+ flex: 1;
9529
+ min-width: 0;
9530
+ display: flex;
9531
+ align-items: stretch;
9532
+ gap: 10px;
9533
+ padding: 10px 11px 10px 0;
9534
+ background: transparent;
9535
+ border: none;
9536
+ color: inherit;
9537
+ cursor: pointer;
9538
+ text-align: left;
9539
+ font-family: inherit;
9540
+ transition: transform 0.06s ease;
9541
+ }
9542
+ .ticket-card-open:active {
9340
9543
  transform: scale(0.99);
9341
9544
  }
9545
+ /* Trailing button that dispatches the ticket to the coding terminal. */
9546
+ .ticket-card-send {
9547
+ flex-shrink: 0;
9548
+ width: 36px;
9549
+ align-self: stretch;
9550
+ display: flex;
9551
+ align-items: center;
9552
+ justify-content: center;
9553
+ background: transparent;
9554
+ border: none;
9555
+ border-left: 1px solid #262a3f;
9556
+ color: #565f89;
9557
+ cursor: pointer;
9558
+ transition: background 0.13s ease, color 0.13s ease, transform 0.06s ease;
9559
+ }
9560
+ .ticket-card-send:hover {
9561
+ background: #1b2030;
9562
+ color: #7aa2f7;
9563
+ }
9564
+ .ticket-card-send:active {
9565
+ transform: scale(0.9);
9566
+ }
9342
9567
  .ticket-card-bar {
9343
9568
  width: 3px;
9344
9569
  border-radius: 3px;