Skip to content

Commit 844a681

Browse files
committed
Copy prompts
1 parent 6e6b673 commit 844a681

File tree

6 files changed

+250
-2
lines changed

6 files changed

+250
-2
lines changed

src/assets/css/ai-guides.css

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/* Styling for the AI-Powered Guides components */
2+
3+
.rfai-container {
4+
background-color: var(--color-background-secondary);
5+
border-radius: var(--border-radius-rounded);
6+
padding: var(--size-medium);
7+
margin: var(--size-large) 0;
8+
border: 1px solid var(--color-grey-light);
9+
}
10+
11+
@media (prefers-color-scheme: dark) {
12+
.rfai-container {
13+
border-color: var(--color-grey-dark);
14+
}
15+
}
16+
17+
.rfai-container h3 {
18+
margin-top: 0;
19+
font-size: var(--type-large);
20+
color: var(--color-text);
21+
text-transform: none; /* Override default h3 uppercase */
22+
}
23+
24+
.rfai-container > p {
25+
margin-bottom: var(--size-medium);
26+
}
27+
28+
.rfai-prompt {
29+
border-top: 1px solid var(--color-grey-light);
30+
padding: var(--size-small) 0;
31+
}
32+
33+
@media (prefers-color-scheme: dark) {
34+
.rfai-prompt {
35+
border-top-color: var(--color-grey-dark);
36+
}
37+
}
38+
39+
.rfai-prompt:first-of-type {
40+
border-top: none;
41+
padding-top: 0;
42+
}
43+
44+
.rfai-prompt:last-of-type {
45+
padding-bottom: 0;
46+
}
47+
48+
.rfai-prompt summary {
49+
display: flex;
50+
justify-content: space-between;
51+
align-items: center;
52+
cursor: pointer;
53+
list-style: none;
54+
}
55+
56+
.rfai-prompt summary::-webkit-details-marker {
57+
display: none;
58+
}
59+
60+
.rfai-prompt-title {
61+
font-weight: var(--weight-bold);
62+
font-size: var(--type-body);
63+
font-family: var(--font-body);
64+
}
65+
66+
.rfai-prompt-subtitle {
67+
margin-top: var(--size-3xsmall);
68+
font-size: var(--type-small);
69+
}
70+
71+
.rfai-copy-button {
72+
background-color: var(--color-theme);
73+
color: var(--color-black);
74+
border: none;
75+
padding: var(--size-2xsmall) var(--size-small);
76+
border-radius: var(--border-radius-rounded);
77+
cursor: pointer;
78+
font-weight: var(--weight-semi-bold);
79+
font-family: var(--font-title);
80+
transition: background-color 0.2s, filter 0.2s;
81+
white-space: nowrap;
82+
text-transform: uppercase;
83+
}
84+
85+
.rfai-copy-button:hover {
86+
filter: brightness(1.1);
87+
}
88+
89+
.rfai-copy-button.copied {
90+
background-color: var(--color-positive);
91+
color: var(--color-white);
92+
}
93+
94+
.rfai-prompt-content {
95+
margin-top: var(--size-small);
96+
}
97+
98+
.rfai-prompt-content pre {
99+
white-space: pre-wrap;
100+
word-wrap: break-word;
101+
background-color: rgba(100, 100, 100, 0.1);
102+
padding: var(--size-xsmall);
103+
border-radius: var(--border-radius-rounded);
104+
max-height: 400px;
105+
overflow-y: auto;
106+
border: none;
107+
}
108+
109+
.rfai-loader, .rfai-error {
110+
color: var(--color-text);
111+
font-style: italic;
112+
padding: var(--size-xsmall);
113+
}
114+
115+
.rfai-error a {
116+
color: var(--color-link);
117+
text-decoration: underline;
118+
}
119+
120+
/* Accessibility class */
121+
.visually-hidden {
122+
position: absolute;
123+
width: 1px;
124+
height: 1px;
125+
padding: 0;
126+
margin: -1px;
127+
overflow: hidden;
128+
clip: rect(0, 0, 0, 0);
129+
white-space: nowrap;
130+
border: 0;
131+
}

src/assets/css/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
@import 'text.css';
99
@import 'modifiers.css';
1010
@import 'transitions.css';
11+
@import 'ai-guides.css';
1112

1213
html, body {
1314
min-height: 100vh;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<template>
2+
<div class="rfai-container">
3+
<h3><span aria-hidden="true">🤖 </span>AI-Powered Guides</h3>
4+
<p>Get a head start with our expert-crafted guides. Just copy a prompt and paste it into your favorite AI chatbot.</p>
5+
6+
<PromptGuide
7+
title="1. Set Up Your Environment"
8+
subtitle="Get a personal guide to installing Python, RF, and Browser library."
9+
prompt-url="https://raw.githubusercontent.com/MarketSquare/rf-prompt-forge/main/prompts/01-setup/01-environment-setup.md"
10+
plausible-event="Copy Setup Prompt"
11+
/>
12+
13+
<PromptGuide
14+
title="2. Create Your First Browser Test"
15+
subtitle="Learn how to write a modern web test with an AI expert."
16+
prompt-url="https://raw.githubusercontent.com/MarketSquare/rf-prompt-forge/main/prompts/02-test-creation/01-browser-test.md"
17+
plausible-event="Copy Test Creation Prompt"
18+
/>
19+
</div>
20+
</template>
21+
22+
<script setup>
23+
import PromptGuide from './PromptGuide.vue'
24+
</script>

src/components/PromptGuide.vue

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<template>
2+
<details class="rfai-prompt">
3+
<summary>
4+
<div>
5+
<div class="rfai-prompt-title">{{ title }}</div>
6+
<div class="rfai-prompt-subtitle">{{ subtitle }}</div>
7+
</div>
8+
<button
9+
class="rfai-copy-button"
10+
@click.prevent.stop="copyAndTrack"
11+
:class="{ copied: isCopied }"
12+
>
13+
<span aria-live="polite" class="visually-hidden">{{ copyStatusMessage }}</span>
14+
<span aria-hidden="true">{{ isCopied ? 'Copied!' : 'Copy Prompt' }}</span>
15+
</button>
16+
</summary>
17+
<div class="rfai-prompt-content">
18+
<div v-if="isLoading" class="rfai-loader">Loading prompt...</div>
19+
<div v-else-if="error" class="rfai-error" v-html="error" />
20+
<pre v-else>{{ promptText }}</pre>
21+
</div>
22+
</details>
23+
</template>
24+
25+
<script setup>
26+
import { ref, onMounted } from 'vue'
27+
28+
const props = defineProps({
29+
title: String,
30+
subtitle: String,
31+
promptUrl: String,
32+
plausibleEvent: String
33+
})
34+
35+
const isLoading = ref(true)
36+
const promptText = ref('')
37+
const error = ref(null)
38+
const isCopied = ref(false)
39+
const copyStatusMessage = ref('')
40+
41+
const fetchPrompt = async () => {
42+
try {
43+
const response = await fetch(props.promptUrl)
44+
if (!response.ok) {
45+
throw new Error(`Failed to fetch: ${response.status} ${response.statusText}`)
46+
}
47+
promptText.value = await response.text()
48+
} catch (e) {
49+
console.error('Failed to fetch prompt:', e)
50+
error.value = 'Error: Could not load prompt. Please copy it directly from the <a href="https://github.com/MarketSquare/rf-prompt-forge" target="_blank" rel="noopener">source repository</a>.'
51+
} finally {
52+
isLoading.value = false
53+
}
54+
}
55+
56+
const copyAndTrack = () => {
57+
if (isLoading.value || error.value) {
58+
alert('Prompt content is not available for copying.')
59+
return
60+
}
61+
62+
navigator.clipboard.writeText(promptText.value)
63+
.then(() => {
64+
isCopied.value = true
65+
copyStatusMessage.value = 'Copied to clipboard!'
66+
67+
// Fire Plausible event if plausible is available on the window
68+
if (window.plausible && props.plausibleEvent) {
69+
window.plausible(props.plausibleEvent)
70+
}
71+
72+
setTimeout(() => {
73+
isCopied.value = false
74+
copyStatusMessage.value = ''
75+
}, 2000)
76+
})
77+
.catch(err => {
78+
console.error('Failed to copy text:', err)
79+
alert('Automatic copy failed. Please select and copy the text manually.')
80+
})
81+
}
82+
83+
// Fetch the prompt when the component is mounted
84+
onMounted(fetchPrompt)
85+
</script>

src/components/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import ChevronIcon from './icons/ChevronIcon.vue'
2323
import NewTabIcon from './icons/NewTabIcon.vue'
2424
import RobotIcon from './icons/RobotIcon.vue'
2525
import DocumentIcon from './icons/DocumentIcon.vue'
26+
import AIGuidesContainer from './AIGuidesContainer.vue'
27+
import PromptGuide from './PromptGuide.vue'
2628

2729
export {
2830
News,
@@ -49,5 +51,7 @@ export {
4951
ChevronIcon,
5052
NewTabIcon,
5153
RobotIcon,
52-
DocumentIcon
54+
DocumentIcon,
55+
AIGuidesContainer,
56+
PromptGuide
5357
}

src/views/Home.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
</div>
3737
</template>
3838
</tab-box>
39+
<a-i-guides-container />
3940
</page-section>
4041
<hr class="theme">
4142
<!-- resources -->
@@ -87,7 +88,8 @@ import {
8788
TabBox,
8889
Sponsors,
8990
Milestones,
90-
NewsBanner
91+
NewsBanner,
92+
AIGuidesContainer
9193
} from 'Components'
9294
import VideoComponent from 'Components/VideoComponent'
9395
@@ -108,6 +110,7 @@ export default {
108110
Sponsors,
109111
Milestones,
110112
NewsBanner,
113+
AIGuidesContainer,
111114
MonacoEditor: defineAsyncComponent(() => import('Components/Editor.vue'))
112115
}
113116
}

0 commit comments

Comments
 (0)