@farming-labs/astro-theme 0.1.58 → 0.1.60

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/astro-theme",
3
- "version": "0.1.58",
3
+ "version": "0.1.60",
4
4
  "description": "Astro UI components for @farming-labs/docs — layout, sidebar, TOC, search, and theme toggle",
5
5
  "keywords": [
6
6
  "astro",
@@ -109,8 +109,8 @@
109
109
  },
110
110
  "dependencies": {
111
111
  "sugar-high": "^0.9.5",
112
- "@farming-labs/docs": "0.1.58",
113
- "@farming-labs/astro": "0.1.58"
112
+ "@farming-labs/docs": "0.1.60",
113
+ "@farming-labs/astro": "0.1.60"
114
114
  },
115
115
  "peerDependencies": {
116
116
  "astro": ">=4.0.0"
@@ -128,6 +128,7 @@ const localizedParentUrl = withLang(parentUrl);
128
128
 
129
129
  <script>
130
130
  let hoverLinkGlobalsBound = false;
131
+ let promptGlobalsBound = false;
131
132
 
132
133
  function setHoverLinkOpen(root, open) {
133
134
  if (!(root instanceof HTMLElement)) return;
@@ -225,6 +226,137 @@ const localizedParentUrl = withLang(parentUrl);
225
226
  document.addEventListener('focusin', closeOpenHoverLinks);
226
227
  }
227
228
 
229
+ async function fallbackCopyPromptText(text) {
230
+ const textarea = document.createElement('textarea');
231
+ textarea.value = text;
232
+ textarea.setAttribute('readonly', '');
233
+ textarea.style.position = 'fixed';
234
+ textarea.style.top = '0';
235
+ textarea.style.left = '0';
236
+ textarea.style.opacity = '0';
237
+ textarea.style.pointerEvents = 'none';
238
+ document.body.appendChild(textarea);
239
+ textarea.focus();
240
+ textarea.select();
241
+
242
+ let copied = false;
243
+ try {
244
+ copied = document.execCommand('copy');
245
+ } catch {
246
+ copied = false;
247
+ } finally {
248
+ document.body.removeChild(textarea);
249
+ }
250
+
251
+ return copied;
252
+ }
253
+
254
+ function setPromptMenuOpen(root, open) {
255
+ if (!(root instanceof HTMLElement)) return;
256
+ const trigger = root.querySelector('[data-prompt-trigger]');
257
+ const menu = root.querySelector('[data-prompt-menu]');
258
+ if (!(trigger instanceof HTMLElement) || !(menu instanceof HTMLElement)) return;
259
+
260
+ trigger.setAttribute('aria-expanded', String(open));
261
+ menu.hidden = !open;
262
+ }
263
+
264
+ function closeOpenPromptMenus(event) {
265
+ document.querySelectorAll('[data-prompt-dropdown]').forEach((root) => {
266
+ if (!(root instanceof HTMLElement)) return;
267
+ if (event?.target instanceof Node && root.contains(event.target)) return;
268
+ setPromptMenuOpen(root, false);
269
+ });
270
+ }
271
+
272
+ function bindPromptCards() {
273
+ document.querySelectorAll('[data-prompt-card]').forEach((root) => {
274
+ if (!(root instanceof HTMLElement)) return;
275
+ if (root.dataset.fdPromptBound === 'true') return;
276
+ root.dataset.fdPromptBound = 'true';
277
+
278
+ const promptTextNode = root.querySelector('[data-prompt-text]');
279
+ const promptText = promptTextNode?.textContent?.trim() ?? '';
280
+
281
+ const copyButton = root.querySelector('[data-prompt-copy]');
282
+ if (copyButton instanceof HTMLButtonElement && promptText) {
283
+ copyButton.addEventListener('click', async () => {
284
+ const defaultIcon = copyButton.querySelector('.fd-prompt-action-icon');
285
+ const copiedIcon = copyButton.querySelector('.fd-prompt-action-icon-copied');
286
+ const label = copyButton.querySelector('[data-prompt-copy-label]');
287
+ const copiedLabel = label?.getAttribute('data-prompt-copy-label') ?? 'Copied';
288
+ const defaultLabel =
289
+ label?.getAttribute('data-prompt-default-label') ??
290
+ label?.textContent ??
291
+ 'Copy prompt';
292
+
293
+ let copied = false;
294
+
295
+ try {
296
+ if (navigator.clipboard?.writeText) {
297
+ await navigator.clipboard.writeText(promptText);
298
+ copied = true;
299
+ } else {
300
+ copied = await fallbackCopyPromptText(promptText);
301
+ }
302
+ } catch {
303
+ copied = await fallbackCopyPromptText(promptText);
304
+ }
305
+
306
+ if (!copied || !(label instanceof HTMLElement)) return;
307
+
308
+ copyButton.dataset.copied = 'true';
309
+ label.textContent = copiedLabel;
310
+ if (defaultIcon instanceof HTMLElement) defaultIcon.hidden = true;
311
+ if (copiedIcon instanceof HTMLElement) copiedIcon.hidden = false;
312
+
313
+ window.setTimeout(() => {
314
+ copyButton.dataset.copied = 'false';
315
+ label.textContent = defaultLabel;
316
+ if (defaultIcon instanceof HTMLElement) defaultIcon.hidden = false;
317
+ if (copiedIcon instanceof HTMLElement) copiedIcon.hidden = true;
318
+ }, 2000);
319
+ });
320
+ }
321
+
322
+ const directOpen = root.querySelector('[data-prompt-open-direct]');
323
+ if (directOpen instanceof HTMLButtonElement && promptText) {
324
+ directOpen.addEventListener('click', () => {
325
+ const template = directOpen.getAttribute('data-url-template');
326
+ if (!template) return;
327
+ const url = template.replace(/\{prompt\}/g, encodeURIComponent(promptText));
328
+ window.open(url, '_blank', 'noopener,noreferrer');
329
+ });
330
+ }
331
+
332
+ const dropdown = root.querySelector('[data-prompt-dropdown]');
333
+ const trigger = root.querySelector('[data-prompt-trigger]');
334
+ if (dropdown instanceof HTMLElement && trigger instanceof HTMLButtonElement) {
335
+ trigger.addEventListener('click', () => {
336
+ const isOpen = trigger.getAttribute('aria-expanded') === 'true';
337
+ setPromptMenuOpen(dropdown, !isOpen);
338
+ });
339
+ }
340
+
341
+ root.querySelectorAll('[data-prompt-open-provider]').forEach((providerButton) => {
342
+ if (!(providerButton instanceof HTMLButtonElement) || !promptText) return;
343
+ providerButton.addEventListener('click', () => {
344
+ const template = providerButton.getAttribute('data-url-template');
345
+ if (!template) return;
346
+ const url = template.replace(/\{prompt\}/g, encodeURIComponent(promptText));
347
+ window.open(url, '_blank', 'noopener,noreferrer');
348
+ if (dropdown instanceof HTMLElement) setPromptMenuOpen(dropdown, false);
349
+ });
350
+ });
351
+ });
352
+
353
+ if (promptGlobalsBound) return;
354
+ promptGlobalsBound = true;
355
+
356
+ document.addEventListener('pointerdown', closeOpenPromptMenus);
357
+ document.addEventListener('focusin', closeOpenPromptMenus);
358
+ }
359
+
228
360
  function getItemOffset(depth) {
229
361
  if (depth <= 2) return 14;
230
362
  if (depth === 3) return 26;
@@ -236,6 +368,7 @@ const localizedParentUrl = withLang(parentUrl);
236
368
  }
237
369
 
238
370
  function initDocsPage() {
371
+ bindPromptCards();
239
372
  const container = document.querySelector('.fd-page-body');
240
373
  const tocList = document.getElementById('fd-toc-list');
241
374
  const tocAside = document.getElementById('fd-toc');
package/styles/docs.css CHANGED
@@ -790,7 +790,8 @@ samp {
790
790
  display: none !important;
791
791
  }
792
792
 
793
- .fd-page-action-menu-item {
793
+ .fd-page-action-menu-item,
794
+ .fd-prompt-menu-item {
794
795
  display: flex;
795
796
  align-items: center;
796
797
  gap: 0.5rem;
@@ -808,11 +809,6 @@ samp {
808
809
  transition: background 0.1s, color 0.1s;
809
810
  }
810
811
 
811
- .fd-page-action-menu-item:hover {
812
- background: var(--color-fd-accent);
813
- color: var(--color-fd-accent-foreground);
814
- }
815
-
816
812
  .fd-page-action-menu-label {
817
813
  flex: 1;
818
814
  }
@@ -828,6 +824,173 @@ samp {
828
824
  text-decoration: none !important;
829
825
  }
830
826
 
827
+ /* ─── Prompt Cards ─────────────────────────────────────────────────── */
828
+
829
+ .fd-prompt {
830
+ display: flex;
831
+ flex-direction: column;
832
+ gap: 0.875rem;
833
+ margin: 1.25rem 0;
834
+ padding: 1rem;
835
+ border: 1px solid var(--color-fd-border);
836
+ border-radius: 0.75rem;
837
+ background: color-mix(in srgb, var(--color-fd-card) 78%, transparent);
838
+ }
839
+
840
+ .fd-prompt-header {
841
+ display: flex;
842
+ align-items: flex-start;
843
+ gap: 0.75rem;
844
+ }
845
+
846
+ .fd-prompt-icon,
847
+ .fd-prompt-action-icon,
848
+ .fd-prompt-menu-icon {
849
+ display: inline-flex;
850
+ align-items: center;
851
+ justify-content: center;
852
+ flex-shrink: 0;
853
+ }
854
+
855
+ .fd-prompt-icon {
856
+ width: 1.75rem;
857
+ height: 1.75rem;
858
+ border-radius: 0.5rem;
859
+ border: 1px solid color-mix(in srgb, var(--color-fd-border) 80%, transparent);
860
+ background: color-mix(in srgb, var(--color-fd-background) 55%, transparent);
861
+ color: var(--color-fd-foreground);
862
+ }
863
+
864
+ .fd-prompt-icon svg,
865
+ .fd-prompt-action-icon svg,
866
+ .fd-prompt-menu-icon svg {
867
+ width: 1rem;
868
+ height: 1rem;
869
+ }
870
+
871
+ .fd-prompt-copy {
872
+ min-width: 0;
873
+ }
874
+
875
+ .fd-prompt-title {
876
+ margin: 0;
877
+ color: var(--color-fd-foreground);
878
+ font-size: 0.9375rem;
879
+ font-weight: 600;
880
+ line-height: 1.35;
881
+ }
882
+
883
+ .fd-prompt-description {
884
+ margin: 0.25rem 0 0;
885
+ color: var(--color-fd-muted-foreground);
886
+ font-size: 0.875rem;
887
+ line-height: 1.55;
888
+ }
889
+
890
+ .fd-prompt-body {
891
+ margin: 0;
892
+ padding: 0.875rem 1rem;
893
+ border-radius: 0.625rem;
894
+ border: 1px solid color-mix(in srgb, var(--color-fd-border) 82%, transparent);
895
+ background: color-mix(in srgb, var(--color-fd-background) 45%, transparent);
896
+ overflow-x: auto;
897
+ }
898
+
899
+ .fd-prompt-body > pre.fd-prompt-code {
900
+ margin: 0;
901
+ padding: 0 !important;
902
+ border: 0 !important;
903
+ border-radius: 0 !important;
904
+ background: transparent !important;
905
+ box-shadow: none !important;
906
+ color: var(--color-fd-foreground);
907
+ font-size: 0.875rem;
908
+ line-height: 1.65;
909
+ white-space: pre-wrap;
910
+ word-break: break-word;
911
+ font-family: var(
912
+ --fd-font-mono,
913
+ ui-monospace,
914
+ "SF Mono",
915
+ SFMono-Regular,
916
+ Menlo,
917
+ Consolas,
918
+ monospace
919
+ );
920
+ }
921
+
922
+ .fd-prompt-actions {
923
+ display: flex;
924
+ flex-wrap: wrap;
925
+ gap: 0.5rem;
926
+ align-items: center;
927
+ justify-content: flex-end;
928
+ }
929
+
930
+ .fd-prompt-action-btn {
931
+ display: inline-flex;
932
+ align-items: center;
933
+ gap: 0.4375rem;
934
+ min-height: 2rem;
935
+ padding: 0.375rem 0.75rem;
936
+ border: 1px solid var(--color-fd-border);
937
+ border-radius: 0.375rem;
938
+ background: var(--color-fd-secondary);
939
+ color: var(--color-fd-muted-foreground);
940
+ font-size: 0.8125rem;
941
+ font-weight: 500;
942
+ line-height: 1;
943
+ cursor: pointer;
944
+ transition: color 0.15s, background 0.15s, border-color 0.15s;
945
+ }
946
+
947
+ .fd-prompt-action-btn:hover {
948
+ color: var(--color-fd-accent-foreground);
949
+ background: var(--color-fd-accent);
950
+ }
951
+
952
+ .fd-prompt-action-btn[data-copied="true"] {
953
+ color: var(--color-fd-foreground);
954
+ }
955
+
956
+ .fd-prompt-action-icon-copied[hidden] {
957
+ display: none !important;
958
+ }
959
+
960
+ .fd-prompt-dropdown {
961
+ position: relative;
962
+ }
963
+
964
+ .fd-prompt-menu {
965
+ position: absolute;
966
+ top: calc(100% + 0.375rem);
967
+ right: 0;
968
+ z-index: 50;
969
+ min-width: 220px;
970
+ padding: 0.375rem;
971
+ background: var(--color-fd-popover, var(--color-fd-background));
972
+ border: 1px solid var(--color-fd-border);
973
+ border-radius: 0.5rem;
974
+ box-shadow: 0 4px 24px hsl(0 0% 0% / 0.15);
975
+ display: flex;
976
+ flex-direction: column;
977
+ gap: 0.125rem;
978
+ }
979
+
980
+ .fd-prompt-menu[hidden] {
981
+ display: none !important;
982
+ }
983
+
984
+ .fd-page-action-menu-item:hover,
985
+ .fd-prompt-menu-item:hover {
986
+ background: var(--color-fd-accent);
987
+ color: var(--color-fd-accent-foreground);
988
+ }
989
+
990
+ .fd-prompt-menu-label {
991
+ flex: 1;
992
+ }
993
+
831
994
  /* ─── Theme Toggle ───────────────────────────────────────────────────── */
832
995
 
833
996
  .fd-theme-toggle {
@@ -780,6 +780,66 @@ code:not(pre code) {
780
780
  color: #1f2937;
781
781
  }
782
782
 
783
+ /* ─── Prompt (pixel-border theme) ──────────────────────────────── */
784
+
785
+ .fd-prompt {
786
+ border-radius: 0 !important;
787
+ border: 1px solid var(--color-fd-border, #262626) !important;
788
+ background: var(--color-fd-card, var(--color-fd-background)) !important;
789
+ box-shadow: 4px 4px 0 0 var(--color-fd-border, #262626) !important;
790
+ }
791
+
792
+ .fd-prompt-body {
793
+ border-radius: 0 !important;
794
+ border-top: 1px solid var(--color-fd-border, #262626) !important;
795
+ background: var(--color-fd-background, #0c0c0c) !important;
796
+ }
797
+
798
+ .fd-prompt-action-btn {
799
+ text-transform: uppercase !important;
800
+ box-shadow: 2px 2px 0 0 var(--color-fd-border, #262626) !important;
801
+ font-family: var(--fd-font-mono, ui-monospace, monospace) !important;
802
+ border-radius: 0 !important;
803
+ font-size: 0.75rem !important;
804
+ letter-spacing: 0.03em !important;
805
+ background: var(--color-fd-background) !important;
806
+ color: var(--color-fd-foreground) !important;
807
+ border: 1px solid var(--color-fd-border) !important;
808
+ }
809
+
810
+ .fd-prompt-action-btn:hover {
811
+ background: var(--color-fd-muted) !important;
812
+ color: var(--color-fd-foreground) !important;
813
+ }
814
+
815
+ .fd-prompt-menu {
816
+ border-radius: 0 !important;
817
+ box-shadow: 4px 4px 0 0 var(--color-fd-border, hsl(0 0% 15%)), 0 4px 24px hsl(0 0% 0% / 0.5) !important;
818
+ background: var(--color-fd-popover) !important;
819
+ border: 2px solid var(--color-fd-border) !important;
820
+ padding: 0.375rem !important;
821
+ }
822
+
823
+ .fd-prompt-menu-item {
824
+ border-radius: 0 !important;
825
+ font-size: 0.8rem !important;
826
+ font-family: var(--fd-font-mono, ui-monospace, monospace) !important;
827
+ text-transform: uppercase !important;
828
+ letter-spacing: 0.03em !important;
829
+ color: var(--color-fd-popover-foreground) !important;
830
+ background: transparent !important;
831
+ border-top: 1px solid var(--color-fd-border) !important;
832
+ }
833
+
834
+ .fd-prompt-menu-item:first-child {
835
+ border-top: none !important;
836
+ }
837
+
838
+ .fd-prompt-menu-item:hover {
839
+ background: var(--color-fd-muted) !important;
840
+ color: var(--color-fd-foreground) !important;
841
+ }
842
+
783
843
  /* ─── Feedback (pixel-border theme) ──────────────────────────────── */
784
844
 
785
845
  .fd-feedback-input,