@comty.io/core 0.1.5 → 0.1.7

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/src/style.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export function getComtyStyle() {
2
2
  const toolbarSurface = "#2b2d2f";
3
3
  const toolbarMuted = "#b8babb";
4
- const commentCursor = `<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M20 2C30 2 38 10 38 20S30 38 20 38H3V28C2 25 2 23 2 20 2 10 10 2 20 2Z" fill="white" stroke="#d6dbe2"/><circle cx="20" cy="20" r="14" fill="#e6e6e6"/><path d="M20 14v12m-6-6h12" fill="none" stroke="#404040" stroke-width="2.5" stroke-linecap="round"/></svg>`;
4
+ const commentCursor = `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 40 40"><path d="M20 2C30 2 38 10 38 20S30 38 20 38H3V28C2 25 2 23 2 20 2 10 10 2 20 2Z" fill="white" stroke="#d6dbe2"/><circle cx="20" cy="20" r="14" fill="#e6e6e6"/><path d="M20 14v12m-6-6h12" fill="none" stroke="#404040" stroke-width="2.5" stroke-linecap="round"/></svg>`;
5
5
  const typographyCursor = `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect x="8" y="8" width="23" height="23" rx="7" fill="${toolbarSurface}" stroke="${toolbarMuted}" stroke-width=".75"/><path d="m14 25 5.5-12L25 25m-8.5-4h6M13 25h4m5 0h4" fill="none" stroke="${toolbarMuted}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="m2 2 2 15 4-5 6-3Z" fill="${toolbarSurface}" stroke="${toolbarMuted}" stroke-width="1.5" stroke-linejoin="round"/></svg>`;
6
6
  return `
7
7
  #comty-root {
@@ -11,7 +11,7 @@ export function getComtyStyle() {
11
11
  --comty-muted: var(--comty-toolbar-muted);
12
12
  --comty-accent: #55aaf5;
13
13
  --comty-error: #ef7676;
14
- --comty-outline: var(--comty-toolbar-muted);
14
+ --comty-outline: var(--comty-accent);
15
15
  --comty-comment-focus: var(--comty-toolbar-muted);
16
16
  --comty-paper: #fff;
17
17
  --comty-ink: #171717;
@@ -48,6 +48,10 @@ export function getComtyStyle() {
48
48
  --comty-toolbar-selected: #505457;
49
49
  --comty-toolbar-muted: ${toolbarMuted};
50
50
  --comty-measure: #ff3b30;
51
+ --comty-markup-yellow: #facc15;
52
+ --comty-markup-green: #22c55e;
53
+ --comty-color-black: #000;
54
+ --comty-color-spectrum: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);
51
55
  --comty-font: "Inter", ui-sans-serif, system-ui, sans-serif;
52
56
  --comty-dialog-surface: var(--comty-surface);
53
57
  --comty-dialog-ink: var(--comty-foreground);
@@ -58,6 +62,8 @@ export function getComtyStyle() {
58
62
  --comty-dialog-width: 32rem;
59
63
  --comty-dialog-shadow: 0 1.5rem 4rem rgb(0 0 0 / .16);
60
64
  --comty-motion-duration: 220ms;
65
+ --comty-working-dot-size: .25em;
66
+ --comty-working-dot-gap: .125em;
61
67
  --comty-ease-out: cubic-bezier(0.23, 1, 0.32, 1);
62
68
  --comty-ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
63
69
  position: fixed;
@@ -69,13 +75,80 @@ export function getComtyStyle() {
69
75
  font-family: var(--comty-font);
70
76
  color-scheme: dark;
71
77
  }
72
-
78
+ #comty-root :is(button, .comty-comment-avatar) { user-select: none; -webkit-user-select: none; }
79
+ #comty-root .comty-comment-avatar img { -webkit-user-drag: none; }
80
+ #comty-root .comty-pin .comty-comment-avatar { pointer-events: none; }
81
+ #comty-root > .comty-agent-panel { position: fixed; right: 1rem; bottom: 5rem; z-index: 2147483647; width: min(22rem, calc(100vw - 2rem)); max-height: min(32rem, calc(100vh - 6rem)); box-sizing: border-box; display: flex; flex-direction: column; overflow: hidden; pointer-events: auto; border: .0625rem solid var(--comty-border); border-radius: 1rem; background: var(--comty-surface); box-shadow: var(--comty-floating-shadow); color: var(--comty-foreground); font: .875rem/1.4 ui-sans-serif, system-ui, sans-serif; transform: translateY(0); opacity: 1; transition: transform var(--comty-motion-duration) var(--comty-ease-out), opacity var(--comty-motion-duration) ease; color-scheme: dark; }
82
+ #comty-root > .comty-agent-panel[data-side="left"] { left: 1rem; right: auto; }
83
+ #comty-root > .comty-agent-panel[data-closed] { transform: translateY(.5rem); opacity: 0; pointer-events: none; }
84
+ #comty-root > .comty-agent-panel[hidden] { display: none; }
85
+ .comty-agent-panel *, .comty-agent-panel *::before { box-sizing: border-box; }
86
+ .comty-agent-panel-header { display: flex; flex: 0 0 auto; align-items: center; gap: .75rem; padding: .5rem .75rem; border-bottom: .0625rem solid var(--comty-hover-surface); }
87
+ .comty-agent-brand { display: grid; place-items: center; width: 2.375rem; height: 2.375rem; flex-shrink: 0; border-radius: .6875rem; color: var(--comty-accent); background: var(--comty-hover-surface); }
88
+ .comty-agent-brand.comty-agent-avatar { width: 2.375rem; height: 2.375rem; border-radius: 0; background: transparent; }
89
+ .comty-agent-panel svg { display: block; width: 1rem; height: 1rem; flex-shrink: 0; }
90
+ .comty-agent-brand svg { width: 1.25rem; height: 1.25rem; }
91
+ .comty-agent-brand.comty-agent-avatar svg { width: 100%; height: 100%; }
92
+ .comty-agent-answer { display: flex; align-items: flex-start; gap: .5rem; padding: .375rem .5rem .875rem; }
93
+ .comty-agent-answer .comty-agent-brand { width: 1.5rem; height: 1.5rem; }
94
+ .comty-agent-answer p { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--comty-foreground); }
95
+ .comty-agent-heading { flex: 1; min-width: 0; }
96
+ .comty-agent-heading strong { display: block; font-weight: 600; font-size: .875rem; }
97
+ .comty-agent-panel-content { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: var(--comty-hover-surface) transparent; padding: .375rem .5rem 2rem; }
98
+ .comty-agent-item { padding: .375rem .25rem; overflow-wrap: anywhere; }
99
+ .comty-agent-item + .comty-agent-item { border-top: .0625rem solid var(--comty-hover-surface); }
100
+ .comty-agent-item h3 { margin: 0 0 .25rem; color: var(--comty-foreground); font: 500 .875rem/1.4 ui-sans-serif, system-ui, sans-serif; }
101
+ .comty-agent-list { display: grid; gap: .125rem; }
102
+ .comty-agent-row { position: relative; display: flex; align-items: center; gap: .625rem; min-height: 2rem; padding: .25rem .5rem; border-radius: .5rem; color: var(--comty-foreground); }
103
+ .comty-agent-row:hover { background: var(--comty-hover-surface); }
104
+ .comty-agent-dot { width: .4375rem; height: .4375rem; flex: 0 0 auto; border-radius: 50%; background: var(--comty-muted); }
105
+ .comty-working-indicator { display: inline-grid; grid-template-columns: repeat(3, var(--comty-working-dot-size)); gap: var(--comty-working-dot-gap); flex: 0 0 auto; color: var(--comty-foreground); }
106
+ .comty-working-indicator > i { display: block; width: var(--comty-working-dot-size); height: var(--comty-working-dot-size); border-radius: 50%; background: currentColor; opacity: .25; transition: opacity 150ms linear; }
107
+ .comty-working-indicator > i[data-active] { opacity: 1; }
108
+ .comty-agent-dot[data-status="success"] { background: var(--comty-agent-success); }
109
+ .comty-agent-dot[data-status="error"] { background: var(--comty-agent-error); }
110
+ .comty-agent-row-name { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
111
+ .comty-agent-row-state { flex: 0 0 auto; color: var(--comty-muted); font-size: .875rem; }
112
+ .comty-agent-row-actions { display: flex; flex-shrink: 0; gap: .25rem; }
113
+ .comty-agent-panel .comty-agent-row-actions .comty-agent-action { min-height: 1.5rem; padding: .125rem .375rem; border-radius: .25rem; background: transparent; color: var(--comty-muted); font-size: .875rem; }
114
+ .comty-agent-panel .comty-agent-row-actions .comty-agent-action:hover { color: var(--comty-foreground); background: var(--comty-surface); }
115
+ #comty-root > .comty-agent-task-toast { position: fixed; right: 1rem; bottom: 1rem; z-index: 2147483647; width: min(20rem, calc(100vw - 2rem)); box-sizing: border-box; padding: .75rem 2rem .75rem .875rem; border: .0625rem solid var(--comty-border); border-radius: .75rem; background: var(--comty-surface); box-shadow: var(--comty-floating-shadow); color: var(--comty-foreground); font: .75rem/1.5 ui-sans-serif, system-ui, sans-serif; pointer-events: auto; opacity: 1; transform: translateY(0); transition: opacity 200ms ease, transform 200ms var(--comty-ease-out); animation: comty-agent-toast-in 200ms var(--comty-ease-out); color-scheme: dark; }
116
+ #comty-root > .comty-agent-task-toast[data-toolbar] { right: auto; bottom: auto; }
117
+ #comty-root > .comty-agent-task-toast[data-leaving] { opacity: 0; transform: translateY(.25rem); pointer-events: none; }
118
+ .comty-agent-task-toast strong { display: block; font-size: .8125rem; font-weight: 600; }
119
+ .comty-agent-task-toast p { margin: .25rem 0 0; color: var(--comty-muted); overflow-wrap: anywhere; }
120
+ .comty-agent-task-toast button { position: absolute; top: .5rem; right: .375rem; display: flex; align-items: center; justify-content: center; width: 1.5rem; height: 1.5rem; padding: 0; border: 0; border-radius: .25rem; background: transparent; color: var(--comty-muted); cursor: pointer; }
121
+ .comty-agent-task-toast button:hover { background: var(--comty-hover-surface); color: var(--comty-foreground); }
122
+ .comty-agent-task-toast button:focus-visible { outline: .125rem solid var(--comty-accent); outline-offset: .125rem; }
123
+ .comty-agent-task-toast svg { width: .875rem; height: .875rem; }
124
+ @keyframes comty-agent-toast-in { from { opacity: 0; transform: translateY(.25rem); } to { opacity: 1; transform: translateY(0); } }
125
+ .comty-agent-panel p { margin: .5rem 0 0; font: inherit; color: var(--comty-muted); }
126
+ .comty-agent-error { margin-top: .75rem; }
127
+ .comty-agent-error strong { display: block; font-weight: 500; color: var(--comty-foreground); }
128
+ .comty-agent-error p { margin-top: .25rem; }
129
+ .comty-agent-banner { margin-bottom: 1rem; }
130
+ .comty-agent-banner .comty-agent-error { margin-top: 0; }
131
+ .comty-agent-task-actions { display: flex; justify-content: flex-end; gap: .375rem; padding: .375rem .625rem; border-top: .0625rem solid var(--comty-border); }
132
+ .comty-agent-actions { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .875rem; }
133
+ :is(.comty-agent-panel, .comty-agent-task-thread) .comty-agent-action { display: inline-flex; align-items: center; justify-content: center; gap: .375rem; min-height: 2rem; margin: 0; padding: .375rem .625rem; border: 0; border-radius: .5rem; font: 500 .875rem/1.4 ui-sans-serif, system-ui, sans-serif; cursor: pointer; color: var(--comty-foreground); background: var(--comty-hover-surface); transition: background-color 150ms ease; }
134
+ .comty-agent-task-thread .comty-agent-action { min-height: 1.5rem; padding: .125rem .5rem; border-radius: .375rem; font-size: .75rem; }
135
+ :is(.comty-agent-panel, .comty-agent-task-thread) .comty-agent-action:hover { background: color-mix(in srgb, var(--comty-foreground) 12%, var(--comty-hover-surface)); }
136
+ :is(.comty-agent-panel, .comty-agent-task-thread) .comty-agent-action[data-primary] { color: var(--comty-paper); background: var(--comty-accent); }
137
+ :is(.comty-agent-panel, .comty-agent-task-thread) .comty-agent-action:disabled { opacity: .5; cursor: wait; }
138
+ .comty-agent-panel .comty-agent-close { width: 1.5rem; min-height: 1.5rem; padding: 0; background: transparent; color: var(--comty-muted); }
139
+ :is(.comty-agent-panel, .comty-agent-task-thread) :is(button, summary):focus-visible { outline: .125rem solid var(--comty-accent); outline-offset: .1875rem; }
140
+ .comty-agent-details { margin-top: .625rem; }
141
+ .comty-agent-panel summary { cursor: pointer; color: var(--comty-muted); font-size: .875rem; }
142
+ .comty-agent-panel pre { margin: .5rem 0 0; padding: .625rem; white-space: pre-wrap; overflow-wrap: anywhere; max-height: 10rem; overflow: auto; border-radius: .5rem; background: var(--comty-surface); color: var(--comty-muted); font: .875rem/1.4 ui-monospace, monospace; }
143
+ .comty-agent-working { position: fixed; z-index: 1; box-sizing: border-box; border: 2px solid var(--comty-accent); border-radius: 0; pointer-events: none; }
144
+ .comty-agent-working-badge { position: absolute; left: -.125rem; bottom: 100%; display: inline-flex; align-items: center; gap: .4rem; padding: .25rem .5rem; border-radius: .375rem .375rem 0 0; background: var(--comty-surface); color: var(--comty-foreground); font: 600 .6875rem/1.4 ui-sans-serif, system-ui, sans-serif; white-space: nowrap; }
145
+ .comty-agent-working[data-badge-below] .comty-agent-working-badge { bottom: auto; top: 100%; border-radius: 0 0 .375rem .375rem; }
73
146
  #comty-root { cursor: default; }
74
147
  #comty-root::backdrop { background: transparent; pointer-events: none; }
75
148
  html.comty-comment-placement,
76
149
  html.comty-comment-placement body,
77
150
  html.comty-comment-placement body *:not(#comty-root):not(#comty-root *) {
78
- cursor: url("data:image/svg+xml,${encodeURIComponent(commentCursor)}") 20 20, crosshair !important;
151
+ cursor: url("data:image/svg+xml,${encodeURIComponent(commentCursor)}") 16 16, crosshair !important;
79
152
  }
80
153
  #comty-root > .comty-sidepanel-open, #comty-root > .comty-pin, #comty-root > .comty-bubble, #comty-root > .comty-create-bubble, #comty-root > .comty-popover, #comty-root > .comty-auth-backdrop, #comty-root > .comty-auth-toast { pointer-events: auto; }
81
154
  .comty-toolbar {
@@ -142,7 +215,7 @@ export function getComtyStyle() {
142
215
  #comty-root [hidden] { display: none !important; }
143
216
  [data-comty-anchor-hidden] { visibility: hidden !important; pointer-events: none !important; }
144
217
  @media (prefers-reduced-motion: reduce) {
145
- .comty-toolbar, .comty-control-button, .comty-sidepanel, .comty-pin, .comty-bubble { transition: none !important; animation: none !important; }
218
+ .comty-toolbar, .comty-control-button, .comty-sidepanel, .comty-pin, .comty-bubble, .comty-agent-panel, .comty-agent-action, .comty-agent-dot, .comty-working-indicator > i, #comty-root > .comty-agent-task-toast { transition: none !important; animation: none !important; }
146
219
  }
147
220
 
148
221
  .comty-sidepanel {
@@ -324,6 +397,16 @@ export function getComtyStyle() {
324
397
  white-space: nowrap;
325
398
  }
326
399
 
400
+ .comty-anchor-badge {
401
+ display: inline-block;
402
+ margin-top: .375rem;
403
+ padding: .125rem .375rem;
404
+ border: 1px solid var(--comty-border);
405
+ border-radius: 999rem;
406
+ color: var(--comty-muted);
407
+ font: .6875rem/1.4 sans-serif;
408
+ }
409
+
327
410
  .comty-pin {
328
411
  position: fixed;
329
412
  z-index: 2147483645;
@@ -605,6 +688,8 @@ export function getComtyStyle() {
605
688
  height: 100%;
606
689
  object-fit: cover;
607
690
  }
691
+ .comty-comment-avatar.comty-agent-avatar { overflow: visible; border-radius: 0; background: transparent; color: var(--comty-paper); }
692
+ .comty-comment-avatar.comty-agent-avatar svg { display: block; width: 100%; height: 100%; }
608
693
 
609
694
  .comty-pin-avatar {
610
695
  width: 24px;
@@ -719,12 +804,12 @@ export function getComtyStyle() {
719
804
  }
720
805
 
721
806
  .comty-popover-item.comty-popover-item-destructive {
722
- color: var(--comty-error);
807
+ color: var(--comty-agent-error);
723
808
  }
724
809
 
725
810
  .comty-popover-item.comty-popover-item-destructive:hover,
726
811
  .comty-popover-item.comty-popover-item-destructive:focus-visible {
727
- background: color-mix(in srgb, var(--comty-error) 12%, var(--comty-surface));
812
+ background: color-mix(in srgb, var(--comty-agent-error) 12%, var(--comty-surface));
728
813
  outline: none;
729
814
  }
730
815
 
@@ -853,6 +938,8 @@ export function getComtyStyle() {
853
938
  }
854
939
 
855
940
  .comty-hover-bubble .comty-thread {
941
+ display: grid;
942
+ gap: 1rem;
856
943
  padding: 12px;
857
944
  }
858
945
 
@@ -1095,19 +1182,23 @@ export function getComtyStyle() {
1095
1182
  margin: 4px 0;
1096
1183
  }
1097
1184
 
1098
- html.comty-comments-active .comty-pin {
1185
+ html:is(.comty-comments-active, .comty-inspect-annotations) .comty-pin {
1099
1186
  display: grid;
1100
1187
  }
1101
1188
 
1102
- html.comty-comments-active .comty-pin.comty-filtered-out {
1189
+ html:is(.comty-comments-active, .comty-inspect-annotations) .comty-pin.comty-filtered-out {
1103
1190
  display: none;
1104
1191
  }
1105
1192
 
1106
- html.comty-comments-active .comty-bubble {
1193
+ html:is(.comty-comments-active, .comty-inspect-annotations) .comty-bubble {
1107
1194
  display: block;
1108
1195
  }
1109
1196
 
1110
1197
  .comty-inspection-layer { position: relative; z-index: 2147483647; pointer-events: none; }
1198
+ .comty-markup-layer, .comty-markup-surface { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 2; pointer-events: none; }
1199
+ .comty-markup-surface { pointer-events: auto; cursor: crosshair; touch-action: none; user-select: none; }
1200
+ .comty-markup-surface[aria-busy="true"] { cursor: progress; }
1201
+ .comty-capture-selection { position: absolute; box-sizing: border-box; border: .0625rem solid var(--comty-accent); box-shadow: 0 0 0 100vmax var(--comty-dialog-backdrop); color: var(--comty-paper); text-shadow: 0 .0625rem .125rem var(--comty-ink); font: .6875rem/1.5 var(--comty-font); padding: .25rem; white-space: nowrap; }
1111
1202
  .comty-overlay-tools, .comty-layout-guides, .comty-layout-guide { position: fixed; inset: 0; pointer-events: none; }
1112
1203
  .comty-overlay-tools { overflow: hidden; }
1113
1204
  .comty-layout-guide { display: grid; box-sizing: border-box; }
@@ -1133,6 +1224,38 @@ export function getComtyStyle() {
1133
1224
  .comty-guide-fields label { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
1134
1225
  .comty-tool-panel input, .comty-tool-panel select { min-width: 0; width: 6rem; box-sizing: border-box; border: 0; border-radius: .25rem; padding: .25rem; background: var(--comty-surface); color: var(--comty-foreground); font: inherit; }
1135
1226
  .comty-tool-panel input[type="color"] { height: 1.75rem; padding: .125rem; }
1227
+ .comty-tool-panel.comty-markup-panel { width: max-content; padding: .375rem; border-radius: .875rem; background: var(--comty-toolbar-group); }
1228
+ .comty-markup-controls, .comty-markup-options, .comty-markup-strokes, .comty-markup-colors { display: flex; align-items: center; gap: .125rem; }
1229
+ .comty-markup-controls { justify-content: space-between; }
1230
+ .comty-markup-options { margin-top: .375rem; padding-top: .375rem; border-top: .0625rem solid var(--comty-border); }
1231
+ .comty-markup-divider { flex: 0 0 .0625rem; height: 1rem; margin-inline: .25rem; background: var(--comty-border); }
1232
+ .comty-markup-panel button { flex: 0 0 auto; width: 2rem; height: 2rem; padding: 0; color: var(--comty-toolbar-muted); border-radius: .5rem; }
1233
+ .comty-markup-panel button svg { display: block; flex: 0 0 auto; width: 1rem; height: 1rem; }
1234
+ .comty-markup-panel button[aria-pressed="false"] { opacity: 1; }
1235
+ .comty-markup-panel button[aria-pressed="true"] { color: var(--comty-foreground); background: var(--comty-hover-surface); }
1236
+ .comty-markup-panel button:disabled { opacity: .35; cursor: default; }
1237
+ .comty-markup-panel button:disabled:hover { background: transparent; }
1238
+ .comty-markup-panel button:not(:disabled):active { transform: scale(.97); }
1239
+ .comty-markup-strokes button { width: 1.5rem; height: 1.5rem; }
1240
+ .comty-markup-colors button { width: 1.375rem; height: 1.5rem; background: transparent; }
1241
+ .comty-markup-colors button[aria-pressed="true"] { background: transparent; }
1242
+ .comty-markup-swatch { display: block; width: .875rem; height: .875rem; border-radius: 50%; background: var(--markup-swatch); box-shadow: inset 0 0 0 .0625rem color-mix(in srgb, var(--comty-paper) 15%, transparent); }
1243
+ .comty-markup-colors button[aria-pressed="true"] .comty-markup-swatch { outline: .0625rem solid var(--comty-foreground); outline-offset: .125rem; }
1244
+ .comty-markup-panel button.comty-markup-picker { width: 1rem; padding: 0; position: relative; flex: 0 0 1rem; height: 1rem; margin-inline: .25rem; border-radius: 50%; background: conic-gradient(var(--comty-measure), var(--comty-markup-yellow), var(--comty-markup-green), var(--comty-accent), var(--comty-error), var(--comty-measure)); }
1245
+ .comty-markup-picker[data-selected] { outline: .0625rem solid var(--comty-foreground); outline-offset: .125rem; }
1246
+ .comty-markup-picker:focus-visible { outline: .125rem solid var(--comty-accent); outline-offset: .125rem; }
1247
+ .comty-markup-color { display: grid; gap: .625rem; margin-top: .375rem; padding: .625rem .25rem .25rem; border-top: .0625rem solid var(--comty-border); }
1248
+ .comty-markup-color-field { position: relative; height: 8rem; border-radius: .5rem; background: linear-gradient(to top, var(--comty-color-black), transparent), linear-gradient(to right, var(--comty-paper), transparent), var(--markup-hue); touch-action: none; cursor: crosshair; }
1249
+ .comty-markup-color-field:focus-visible { outline: .125rem solid var(--comty-accent); outline-offset: .125rem; }
1250
+ .comty-markup-color-field > span { position: absolute; width: .625rem; height: .625rem; box-sizing: border-box; border: .125rem solid var(--comty-paper); border-radius: 50%; background: var(--markup-color); box-shadow: 0 0 0 .0625rem var(--comty-ink); transform: translate(-50%, -50%); pointer-events: none; }
1251
+ .comty-markup-color input.comty-markup-hue { appearance: none; -webkit-appearance: none; display: block; width: 100%; height: 1rem; margin: 0; padding: 0; border-radius: 1rem; background: var(--comty-color-spectrum); cursor: pointer; }
1252
+ .comty-markup-hue::-webkit-slider-thumb { appearance: none; -webkit-appearance: none; width: .875rem; height: .875rem; box-sizing: border-box; border: .125rem solid var(--comty-paper); border-radius: 50%; background: var(--markup-hue); box-shadow: 0 0 0 .0625rem var(--comty-ink); }
1253
+ .comty-markup-hue::-moz-range-thumb { width: .875rem; height: .875rem; box-sizing: border-box; border: .125rem solid var(--comty-paper); border-radius: 50%; background: var(--markup-hue); box-shadow: 0 0 0 .0625rem var(--comty-ink); }
1254
+ .comty-markup-hex { display: flex; align-items: center; gap: .5rem; padding: .25rem .5rem; border-radius: .5rem; background: var(--comty-surface); color: var(--comty-muted); font: .6875rem/1.5 var(--comty-font); }
1255
+ .comty-markup-color-preview { flex: 0 0 1rem; height: 1rem; border-radius: .25rem; background: var(--markup-color); box-shadow: inset 0 0 0 .0625rem var(--comty-border); }
1256
+ .comty-markup-hex input { flex: 1; width: 0; text-align: right; font-family: ui-monospace, monospace; }
1257
+ .comty-markup-hex input[aria-invalid="true"] { outline: .0625rem solid var(--comty-error); }
1258
+
1136
1259
  .comty-ruler { position: fixed; background: var(--comty-surface); pointer-events: auto; touch-action: none; }
1137
1260
  .comty-ruler-horizontal { left: 0; top: 0; width: 100vw; height: 1.125rem; border-bottom: .0625rem solid var(--comty-border); cursor: ns-resize; }
1138
1261
  .comty-ruler-vertical { left: 0; top: 0; height: 100vh; width: 1.125rem; border-right: .0625rem solid var(--comty-border); cursor: ew-resize; }
@@ -1207,16 +1330,16 @@ export function getComtyStyle() {
1207
1330
  .comty-motion-panel button[aria-pressed="true"] { opacity: 1; color: var(--comty-paper); background: var(--comty-hover-surface); }
1208
1331
  .comty-motion-panel button[aria-pressed="false"] { opacity: 1; }
1209
1332
  .comty-motion-panel [data-motion-status] { margin: .5rem; font-size: .75rem; color: var(--comty-muted); }
1210
- @media (max-width: 25rem) { #comty-root { --comty-tool-size: 2rem; --comty-toolbar-padding: .25rem; } .comty-toolbar { right: .5rem; bottom: .5rem; max-width: calc(100vw - 1rem); flex-wrap: wrap; justify-content: center; row-gap: .25rem; } .comty-tool-divider { display: none; } }
1333
+ @media (max-width: 38rem) { #comty-root { --comty-tool-size: 2rem; --comty-toolbar-padding: .25rem; } .comty-toolbar { right: .5rem; bottom: .5rem; max-width: calc(100vw - 1rem); flex-wrap: wrap; justify-content: center; row-gap: .25rem; } .comty-tool-divider { display: none; } }
1211
1334
 
1212
1335
  .comty-inspection-box {
1213
1336
  position: fixed;
1214
1337
  box-sizing: border-box;
1215
1338
  border: .0625rem solid var(--comty-outline);
1216
- background: color-mix(in oklch, var(--comty-outline) 8%, transparent);
1339
+ background: transparent;
1340
+ border-radius: 0;
1217
1341
  pointer-events: none;
1218
1342
  }
1219
- .comty-inspection-box[data-selected] { background: color-mix(in oklch, var(--comty-outline) 5%, transparent); }
1220
1343
  .comty-inspect-spacing { position: fixed; pointer-events: none; background: var(--comty-spacing-color); }
1221
1344
  .comty-inspect-margin { --comty-spacing-color: var(--comty-margin-fill); }
1222
1345
  .comty-inspect-padding { --comty-spacing-color: var(--comty-padding-fill); }
@@ -1270,6 +1393,15 @@ export function getComtyStyle() {
1270
1393
  height: 5.75rem;
1271
1394
  border-radius: .75rem;
1272
1395
  }
1396
+ .comty-agent-scope { display: inline-flex; align-items: center; gap: .375rem; color: var(--comty-muted); font: .6875rem/1.25 var(--comty-font); white-space: nowrap; cursor: pointer; pointer-events: auto; opacity: 0; visibility: hidden; }
1397
+ .comty-create-expanded .comty-agent-scope { opacity: 1; visibility: visible; }
1398
+ .comty-agent-scope input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; }
1399
+ .comty-agent-scope-track { position: relative; width: 1.5rem; height: .875rem; flex: 0 0 auto; border-radius: 999rem; background: var(--comty-border); transition: background-color 160ms ease; }
1400
+ .comty-agent-scope-track::after { content: ""; position: absolute; top: .125rem; left: .125rem; width: .625rem; height: .625rem; border-radius: 50%; background: var(--comty-paper); transition: transform 160ms ease; }
1401
+ .comty-agent-scope input:checked + .comty-agent-scope-track { background: var(--comty-accent); }
1402
+ .comty-agent-scope input:checked + .comty-agent-scope-track::after { transform: translateX(.625rem); }
1403
+ .comty-agent-scope input:focus-visible + .comty-agent-scope-track { outline: .125rem solid var(--comty-comment-focus); outline-offset: .125rem; }
1404
+ .comty-agent-thread-error { grid-column: 2; margin: .25rem 0 0; color: var(--comty-agent-error); font: .6875rem/1.3 var(--comty-font); }
1273
1405
 
1274
1406
  .comty-create-bubble.comty-create-has-attachment { height: 7.5rem; }
1275
1407
  .comty-create-bubble.comty-bubble-left { transform-origin: top right; }
package/src/vite.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import type { Plugin } from 'vite';
2
+ import type { ComtyOptions } from './browser.js';
3
+
4
+ /** Development only: leaves production builds untouched. */
5
+ export function comty(options?: Pick<ComtyOptions, 'projectKey' | 'apiUrl'> & { agent?: 'codex' | 'claude' | 'cursor' | 'gemini' | 'grok'; model?: string }): Plugin;
package/src/vite.mjs ADDED
@@ -0,0 +1,69 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { createCompanion, isLoopback } from './companion.mjs';
3
+
4
+ /** Starts the local companion on Vite's own origin, without a second port. */
5
+ export function comty(options = {}) {
6
+ let companion, origin, start;
7
+ return {
8
+ name: 'comty', apply: 'serve',
9
+ configureServer(server) {
10
+ const host = server.config.server.host;
11
+ if (host === true || (typeof host === 'string' && !isLoopback(host))) {
12
+ throw new Error('Comty Agent requires Vite to bind localhost or 127.0.0.1');
13
+ }
14
+ if (!server.httpServer) throw new Error('Comty requires a Vite HTTP server (middleware mode is unsupported)');
15
+ const runtime = new URL('./browser.js', import.meta.url);
16
+ const style = new URL('./style.js', import.meta.url);
17
+ const anchors = new URL('./anchors.js', import.meta.url);
18
+ const capture = new URL('./capture.js', import.meta.url);
19
+ const captureEngine = new URL('./capture-engine.js', import.meta.url);
20
+ const captureResources = new URL('./capture-resources.js', import.meta.url);
21
+ const comments = new URL('./comments.js', import.meta.url);
22
+ const measurements = new URL('./measurements.js', import.meta.url);
23
+ const markup = new URL('./inspect-markup.js', import.meta.url);
24
+ start = new Promise((resolve, reject) => {
25
+ const onError = error => {
26
+ // Vite retries the next port after EADDRINUSE unless strictPort is set.
27
+ if (error.code !== 'EADDRINUSE' || server.config.server.strictPort) reject(error);
28
+ };
29
+ server.httpServer.on('error', onError);
30
+ server.httpServer.once('listening', () => {
31
+ server.httpServer.off('error', onError);
32
+ try {
33
+ const address = server.httpServer.address();
34
+ const hostname = typeof host === 'string' ? host : 'localhost';
35
+ origin = `${server.config.server.https ? 'https' : 'http'}://${hostname}:${address.port}`;
36
+ companion = createCompanion({ cwd: server.config.root, origin, provider: options.agent, model: options.model });
37
+ server.httpServer.once('close', () => { void companion.close().catch(error => server.config.logger.error(`Comty: ${error.message}`)); });
38
+ resolve();
39
+ } catch (error) { reject(error); }
40
+ });
41
+ });
42
+ start.catch(error => server.config.logger.error(`Comty: ${error.message}`));
43
+ server.middlewares.use(async (request, response, next) => {
44
+ if (!request.url?.startsWith('/__comty/')) return next();
45
+ try {
46
+ await start;
47
+ if (request.headers.host !== new URL(origin).host || (request.headers.origin && request.headers.origin !== origin)) {
48
+ response.writeHead(403); response.end(); return;
49
+ }
50
+ const path = new URL(request.url, origin).pathname;
51
+ if (request.method === 'GET' && ['/__comty/browser.js', '/__comty/style.js', '/__comty/anchors.js', '/__comty/capture.js', '/__comty/capture-engine.js', '/__comty/capture-resources.js', '/__comty/comments.js', '/__comty/measurements.js', '/__comty/inspect-markup.js', '/__comty/start.js'].includes(path)) {
52
+ response.setHeader('Content-Type', 'text/javascript'); response.setHeader('Cache-Control', 'no-store');
53
+ response.setHeader('X-Content-Type-Options', 'nosniff');
54
+ response.end(path.endsWith('/browser.js') ? await readFile(runtime) : path.endsWith('/style.js') ? await readFile(style) : path.endsWith('/anchors.js') ? await readFile(anchors) : path.endsWith('/capture.js') ? await readFile(capture) : path.endsWith('/capture-engine.js') ? await readFile(captureEngine) : path.endsWith('/capture-resources.js') ? await readFile(captureResources) : path.endsWith('/comments.js') ? await readFile(comments) : path.endsWith('/measurements.js') ? await readFile(measurements) : path.endsWith('/inspect-markup.js') ? await readFile(markup) :
55
+ `import { initComty } from '/__comty/browser.js';\ninitComty(${JSON.stringify({ projectKey: options.projectKey, apiUrl: options.apiUrl, agent: { url: `${origin}/__comty`, token: companion.token } })}).catch(console.error);`);
56
+ } else await companion.handle(request, response);
57
+ } catch (error) { next(error); }
58
+ });
59
+ },
60
+ transformIndexHtml: {
61
+ order: 'pre',
62
+ async handler() {
63
+ await start;
64
+ return [{ tag: 'script', attrs: { type: 'module', src: '/__comty/start.js', 'vite-ignore': '' }, injectTo: 'head-prepend' }];
65
+ },
66
+ },
67
+ async closeBundle() { await companion?.close(); },
68
+ };
69
+ }