massimoavvisati commited on
Commit
9c96839
·
1 Parent(s): fa117a9
icons/circle.png ADDED

Git LFS Details

  • SHA256: e8bedb9c3f16bbc49111ed17abbd79679ecbc23f3233361904eaa60d0d7a60cc
  • Pointer size: 129 Bytes
  • Size of remote file: 9.18 kB
icons/hand-soft.png ADDED

Git LFS Details

  • SHA256: 8f3a50913d545dd5abbabd15d1ab8cc2b92cb5123794198751b18548ccc22ded
  • Pointer size: 129 Bytes
  • Size of remote file: 1.07 kB
icons/hand.png ADDED

Git LFS Details

  • SHA256: 06f30678a1567c9c7eb83b5042858c4db28ad1ee7e68ce210c01169fa4dd5971
  • Pointer size: 129 Bytes
  • Size of remote file: 1.1 kB
icons/hand.svg ADDED
icons/play.svg ADDED
images/avatar-01.png ADDED

Git LFS Details

  • SHA256: 0d9de8141f0925427c13d06091ac8b42cf8f2f1c04a0686754316a8245474520
  • Pointer size: 132 Bytes
  • Size of remote file: 1.12 MB
images/avatar-02.png ADDED

Git LFS Details

  • SHA256: 7cea7d90351bfdca8a31cdc26c950851e4b3e8f3e7b23a983d9e553a3a37debf
  • Pointer size: 132 Bytes
  • Size of remote file: 1.75 MB
images/avatar-03.png ADDED

Git LFS Details

  • SHA256: adeb002dc8fbdd9d3db9938f88533915ea9cf5a54249e723d9effb77b330d584
  • Pointer size: 131 Bytes
  • Size of remote file: 946 kB
images/avatar-04.png ADDED

Git LFS Details

  • SHA256: 685504d19806458fcb0287fcbc9dd04e548612e63bf500450a3f6eab8bae100e
  • Pointer size: 132 Bytes
  • Size of remote file: 1.64 MB
images/avatar-05.png ADDED

Git LFS Details

  • SHA256: 5c3567dee668022327522c2cb189f6eeca17546e12bae9995c8ea94e3a7a52fe
  • Pointer size: 132 Bytes
  • Size of remote file: 1.85 MB
images/avatar-06.png ADDED

Git LFS Details

  • SHA256: 01cf60069773dd6da46d32c3d5b948ea4a430bbd8fc1aef04053325c94eac1f6
  • Pointer size: 132 Bytes
  • Size of remote file: 1.49 MB
images/avatar-07.png ADDED

Git LFS Details

  • SHA256: 49d75945c46463af9b70309e6bbac14a06fc27094c6dc70e4bbebbd25906d382
  • Pointer size: 131 Bytes
  • Size of remote file: 443 kB
images/deck-01-back.png ADDED

Git LFS Details

  • SHA256: fee6d2810c5109bac562ca15ddfb304844f63550feb5e78b3f17a0b1d794b6a8
  • Pointer size: 131 Bytes
  • Size of remote file: 755 kB
images/deck-01.jpg ADDED

Git LFS Details

  • SHA256: 6e7cdce34a1bfb96fa5ca58adcebc8c784230f4a0e1cb4dd6ffde82a0a7145cd
  • Pointer size: 132 Bytes
  • Size of remote file: 2.03 MB
images/deck-02-back.png ADDED

Git LFS Details

  • SHA256: 557370d1e0f4d4259a03d13f6d718d6049d97032904556d74f7b6ebe98259fb4
  • Pointer size: 131 Bytes
  • Size of remote file: 723 kB
images/deck-02.jpg ADDED

Git LFS Details

  • SHA256: c69fe92b0ce74fe7fb657d458da0ddd4cee68e8411873a365e9a970041d779f5
  • Pointer size: 131 Bytes
  • Size of remote file: 713 kB
images/deck-03-back.png ADDED

Git LFS Details

  • SHA256: 0d2f8bbcd830c4a51ffeb7d71e3e6239d4a3ecb5a776ffb173c6ad6a10d06153
  • Pointer size: 131 Bytes
  • Size of remote file: 675 kB
images/deck-03.jpg ADDED

Git LFS Details

  • SHA256: 3e98e082e64422f64eb2ba8b4a6a5221b9a4a42ec01fec9a5753d41f10961805
  • Pointer size: 132 Bytes
  • Size of remote file: 1.02 MB
images/deck-04-back.png ADDED

Git LFS Details

  • SHA256: 796de1ce08d0082840b0eb61841eacb4516218065a9fa557d1ca9f260f5e9d5a
  • Pointer size: 131 Bytes
  • Size of remote file: 531 kB
images/deck-04.jpg ADDED

Git LFS Details

  • SHA256: ac8b6e8688f656b53c9efad94b3b2a592519d092d752f5104ec5d58834b74f38
  • Pointer size: 131 Bytes
  • Size of remote file: 879 kB
images/deck-05-back.png ADDED

Git LFS Details

  • SHA256: 9980872e981a9e3acd80028899080f9929588f6b35cffa92775a5c2187b23307
  • Pointer size: 131 Bytes
  • Size of remote file: 710 kB
images/deck-05.jpg ADDED

Git LFS Details

  • SHA256: 98d49f58930de5ee92d518dc7cc43acc91d864dd04209421ae908d71d00bf924
  • Pointer size: 132 Bytes
  • Size of remote file: 1.78 MB
images/deck-06-back.png ADDED

Git LFS Details

  • SHA256: aea3e8732d342558e7294a54cdc1da541791cda78e843213e8e3e846a1eb0b57
  • Pointer size: 131 Bytes
  • Size of remote file: 618 kB
images/deck-06.jpg ADDED

Git LFS Details

  • SHA256: 846febe178662964a26103eec70071de3775de0ac53cacf5f4686d4a1bf1549b
  • Pointer size: 131 Bytes
  • Size of remote file: 896 kB
images/deck-07-back.png ADDED

Git LFS Details

  • SHA256: 78a9e88dcf2ba5ce40a7c8834c0972367eb2c04f0897810a7fd1d514438837d5
  • Pointer size: 131 Bytes
  • Size of remote file: 799 kB
images/deck-07.jpg ADDED

Git LFS Details

  • SHA256: 437aa321d24d9121f898c2ab0398622ee05a804d71545a5703d776f86a9ff84d
  • Pointer size: 132 Bytes
  • Size of remote file: 1.45 MB
index.html CHANGED
@@ -2,159 +2,114 @@
2
  <html lang="en">
3
 
4
  <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Tarot Fortune Teller</title>
8
- <style>
9
- body {
10
- font-family: 'Arial', sans-serif;
11
- background-color: #1a1a2e;
12
- color: #e0e0e0;
13
- display: flex;
14
- justify-content: center;
15
- align-items: center;
16
- height: 100vh;
17
- margin: 0;
18
- padding: 20px;
19
- box-sizing: border-box;
20
- }
21
-
22
- .container {
23
- background-color: #16213e;
24
- border-radius: 15px;
25
- padding: 30px;
26
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
27
- text-align: center;
28
- max-width: 500px;
29
- width: 100%;
30
- }
31
-
32
- h1 {
33
- color: #e94560;
34
- margin-bottom: 20px;
35
- }
36
-
37
- textarea {
38
- width: 100%;
39
- height: 100px;
40
- margin-bottom: 20px;
41
- padding: 10px;
42
- border-radius: 5px;
43
- border: none;
44
- background-color: #0f3460;
45
- color: #e0e0e0;
46
- resize: vertical;
47
- }
48
-
49
- button {
50
- background-color: #e94560;
51
- color: #fff;
52
- border: none;
53
- padding: 10px 20px;
54
- border-radius: 5px;
55
- cursor: pointer;
56
- transition: background-color 0.3s;
57
- }
58
-
59
- button:hover {
60
- background-color: #c73e54;
61
- }
62
-
63
- #prediction {
64
- margin-top: 20px;
65
- font-style: italic;
66
- }
67
- </style>
68
  </head>
69
 
70
  <body>
71
- <div class="container">
72
- <h1>Tarot Fortune Teller</h1>
73
- <textarea id="question" placeholder="Enter your question here..."></textarea>
74
- <button onclick="getPrediction()">Reveal Your Fortune</button>
75
- <p id="prediction"></p>
76
- </div>
77
-
78
- <script type="module">
79
- import { Client } from 'https://cdn.jsdelivr.net/npm/@gradio/[email protected]/+esm'
80
- import MarkdownIt from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm'
81
-
82
- const md = new MarkdownIt();
83
-
84
- function drawThreeCards() {
85
- const majorArcana = [
86
- "The Fool",
87
- "The Magician",
88
- "The High Priestess",
89
- "The Empress",
90
- "The Emperor",
91
- "The Hierophant",
92
- "The Lovers",
93
- "The Chariot",
94
- "Strength",
95
- "The Hermit",
96
- "Wheel of Fortune",
97
- "Justice",
98
- "The Hanged Man",
99
- "Death",
100
- "Temperance",
101
- "The Devil",
102
- "The Tower",
103
- "The Star",
104
- "The Moon",
105
- "The Sun",
106
- "Judgement",
107
- "The World"
108
- ];
109
-
110
- // Shuffle the array
111
- const shuffledDeck = majorArcana.sort(() => 0.5 - Math.random());
112
-
113
- // Select the first three cards from the shuffled array
114
- const selectedCards = shuffledDeck.slice(0, 3);
115
-
116
- // Join the selected cards into a single string separated by commas
117
- return selectedCards.join(", ");
118
- }
119
-
120
- window.getPrediction = async function () {
121
- const question = document.getElementById('question').value;
122
- const predictionElement = document.getElementById('prediction');
123
-
124
- if (!question) {
125
- predictionElement.textContent = "Please enter a question first.";
126
- return;
127
- }
128
-
129
- predictionElement.textContent = "Consulting the cards...";
130
-
131
- try {
132
- const client = await Client.connect("hysts/zephyr-7b");
133
- const result = await client.submit("/chat", {
134
- message: `Cards: ${drawThreeCards()} User Question: ${question}`,
135
- system_prompt: "You are an expert fortune teller capable of interpreting tarot cards giving a quick answer and a bit of interpretation in the native language of the User Question",
136
- max_new_tokens: 512,
137
- // temperature: 0.7,
138
- // top_p: 0.95,
139
- // top_k: 50,
140
- // repetition_penalty: 1,
141
- });
142
-
143
- let fullResponse = '';
144
- for await (const msg of result) {
145
- if (msg.type === "data") {
146
- fullResponse = msg.data[0];
147
- predictionElement.innerHTML = md.render(fullResponse);
148
- }
149
- }
150
-
151
- //predictionElement.textContent = result.data[0];
152
- } catch (error) {
153
- console.error('Error:', error);
154
- predictionElement.textContent = "The cards are clouded. Please try again later.";
155
- }
156
- }
157
- </script>
158
  </body>
159
 
160
  </html>
 
2
  <html lang="en">
3
 
4
  <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Tarot AI Experience</title>
8
+ <meta name="description" content="An AI based tarot reading web app">
9
+ <!-- Precarica le immagini utilizzate nel CSS -->
10
+ <link rel="preload" as="image" href="./images/deck-01.jpg">
11
+ <link rel="preload" as="image" href="./images/deck-01-back.png">
12
+ <link rel="preload" as="image" href="./images/deck-02.jpg">
13
+ <link rel="preload" as="image" href="./images/deck-02-back.png">
14
+ <link rel="preload" as="image" href="./images/deck-03.jpg">
15
+ <link rel="preload" as="image" href="./images/deck-03-back.png">
16
+ <link rel="preload" as="image" href="./images/deck-04.jpg">
17
+ <link rel="preload" as="image" href="./images/deck-04-back.png">
18
+ <link rel="preload" as="image" href="./images/deck-05.jpg">
19
+ <link rel="preload" as="image" href="./images/deck-05-back.png">
20
+ <link rel="preload" as="image" href="./images/deck-06.jpg">
21
+ <link rel="preload" as="image" href="./images/deck-06-back.png">
22
+ <link rel="preload" as="image" href="./images/deck-07.jpg">
23
+ <link rel="preload" as="image" href="./images/deck-07-back.png">
24
+ <link rel="stylesheet" href="main.css">
25
+ <script type="module" src="main.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  </head>
27
 
28
  <body>
29
+ <div id="cards-container">
30
+ <!-- Le card vengono generate dinamicamente con JavaScript -->
31
+ </div>
32
+ <dialog id="welcome">
33
+ <div>
34
+ <h1 style="text-align: center; font-size: 2em;">Tarot AI Experience</h1>
35
+ <p style="text-align: center;">An AI based tarot reading web app</p>
36
+ <p style="text-align: center; font-size: 0.8em;">by Massimo Avvisati</p>
37
+ <button id="start" class="continue-button"></button>
38
+ </div>
39
+ </dialog>
40
+ <dialog id="deck-selection">
41
+ <div class="toolbar"><button id="settings-button">•••</button></div>
42
+ <div id="fortune-tellers">
43
+ <div data-fortune-teller-id="01" class="fortune-teller-profile">
44
+ <div class="tag">AI</div>
45
+ <img src="./images/avatar-01.png" alt="Avatar" class="fortune-teller-avatar">
46
+ </div>
47
+ <div data-fortune-teller-id="02" class="fortune-teller-profile">
48
+ <div class="tag">AI</div>
49
+ <img src="./images/avatar-02.png" alt="Avatar" class="fortune-teller-avatar">
50
+ </div>
51
+ <div data-fortune-teller-id="03" class="fortune-teller-profile">
52
+ <div class="tag cpu">AI</div>
53
+ <img src="./images/avatar-03.png" alt="Avatar" class="fortune-teller-avatar">
54
+ </div>
55
+ <div data-fortune-teller-id="04" class="fortune-teller-profile">
56
+ <div class="tag">AI</div>
57
+ <img src="./images/avatar-04.png" alt="Avatar" class="fortune-teller-avatar">
58
+ </div>
59
+ <div data-fortune-teller-id="05" class="fortune-teller-profile">
60
+ <div class="tag">AI</div>
61
+ <img src="./images/avatar-05.png" alt="Avatar" class="fortune-teller-avatar">
62
+ </div>
63
+ <div data-fortune-teller-id="06" class="fortune-teller-profile">
64
+ <div class="tag human">AI</div>
65
+ <img src="./images/avatar-06.png" alt="Avatar" class="fortune-teller-avatar">
66
+ </div>
67
+ <div data-fortune-teller-id="07" class="fortune-teller-profile">
68
+ <div class="tag">AI</div>
69
+ <img src="./images/avatar-07.png" alt="Avatar" class="fortune-teller-avatar">
70
+ </div>
71
+ <div class="fortune-teller-buttons">
72
+ <button>+</button>
73
+ </div>
74
+ <div class="fortune-teller-buttons">
75
+ <button disabled id="fortune-teller-selected" class="continue-button"></button>
76
+ </div>
77
+
78
+ </div>
79
+
80
+ </dialog>
81
+ <dialog id="question">
82
+ <div class="container">
83
+ <!-- Textarea for user input -->
84
+ <textarea id="questionInput" spellcheck="false" rows="4"></textarea>
85
+
86
+ <!-- Button to ask for Tarot Reading -->
87
+ <div class="horizontal-buttons">
88
+ <button id="changeFortuneTellerButton">&laquo;</button>
89
+ <button id="questionButton">?</button>
90
+ </div>
91
+ </div>
92
+ </dialog>
93
+ <dialog id="response">
94
+ <div class="response-container">
95
+ <div class="current-fortune-teller"></div>
96
+ <div id="response-output"><span>.</span><span>.</span><span>.</span></div>
97
+ <div>
98
+ <button id="new-question">+</button>
99
+ </div>
100
+ </dialog>
101
+ <dialog id="settings">
102
+ <div class="toolbar">
103
+ <button id="close-settings-button">✖</button>
104
+ </div>
105
+ <div>
106
+ <div class="setting">
107
+ <label for="llm-server">Tarot LLM HuggingFace Space</label>
108
+ <input id="llm-server" type="text" spellcheck="false" autocomplete="false" value="hysts/zephyr-7b">
109
+
110
+ </div>
111
+ </div>
112
+ </dialog>
 
 
 
113
  </body>
114
 
115
  </html>
main.css ADDED
@@ -0,0 +1,477 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --default-font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
3
+ --rose: #ff0069ff;
4
+ --violet-jtc: #542344ff;
5
+ --steel-blue: #2d82b7ff;
6
+ --magnolia: #fcf7ffff;
7
+ --pearl: #e6e1c5ff;
8
+ --card-size: 0.6;
9
+ --card-effect: linear-gradient(45deg, #0006, #fff3 70%);
10
+ font-family: var(--default-font-family);
11
+ }
12
+
13
+
14
+ body {
15
+ margin: 0;
16
+ background-size: 50%;
17
+ background-repeat: no-repeat;
18
+ background-position: left bottom;
19
+ background-color: #111;
20
+ height: 100lvh;
21
+ }
22
+
23
+ body.background-01 {
24
+ /* background-image: url("./images/background-02.png"), linear-gradient(270deg, #111 0%, #3c5462 90%); */
25
+ background-image: linear-gradient(270deg, #111 0%, #607d8b 90%);
26
+ }
27
+
28
+ body.background-02 {
29
+ /* background-image: url("./images/background-02.png"), linear-gradient(270deg, #111 0%, #3c5462 90%); */
30
+ background-image: linear-gradient(270deg, #111 0%, #3c5462 90%);
31
+ }
32
+
33
+ body.background-03 {
34
+ /* background-image: url("./images/background-02.png"), linear-gradient(270deg, #111 0%, #3c5462 90%); */
35
+ background-image: linear-gradient(270deg, #111 0%, #3c5462 90%);
36
+ }
37
+
38
+ body.background-04 {
39
+ background-image: linear-gradient(270deg, #111 0%, #009688 90%);
40
+
41
+ }
42
+
43
+ body.background-05 {
44
+ background-image: linear-gradient(270deg, #111 0%, #ac1444 90%);
45
+ }
46
+
47
+ body.background-06 {
48
+ background-image: linear-gradient(270deg, #111 0%, #795548 90%);
49
+ }
50
+
51
+
52
+ #fortune-tellers {
53
+ display: grid;
54
+ grid-template-columns: 1fr 1fr 1fr;
55
+ gap: 1rem;
56
+
57
+ }
58
+
59
+ /*
60
+ .fortune-teller-profile {
61
+ grid-template-rows: 30lvh;
62
+ grid-template-columns: 1fr 1fr;
63
+ grid-template-areas:'picture deck'
64
+ 'bio bio';
65
+ max-width: min-content;
66
+ margin-block: 1rem;
67
+ display: grid;
68
+ overflow: auto;
69
+ align-self: center;
70
+ justify-items: center;
71
+ } */
72
+
73
+ .fortune-teller-profile {
74
+ display: flex;
75
+ justify-content: center;
76
+ align-items: center;
77
+ opacity: 0.3;
78
+ filter: grayscale();
79
+ border-radius: 50%;
80
+ border: 3px solid #fff3;
81
+ box-shadow: 0 0 20px #fff0;
82
+ transition: opacity 0.5s allow-discrete, box-shadow 1s;
83
+ aspect-ratio: 1;
84
+ position: relative;
85
+ width: 100%;
86
+ max-height: 20lvh;
87
+ overflow: hidden;
88
+ cursor: url("./icons/hand.png") 19 4, default;
89
+ }
90
+
91
+ .fortune-teller-profile.selected {
92
+ opacity: 1;
93
+ filter: grayscale(0);
94
+ box-shadow: 0 0 20px #ffff;
95
+ }
96
+
97
+ textarea {
98
+ background-color: transparent;
99
+ border: 0;
100
+ border-right: 1px solid #fff6;
101
+ font-size: 1rem;
102
+ min-width: 60lvw;
103
+ max-width: 80lvw;
104
+ font-size: clamp(1rem, 5lvh, 2.5rem);
105
+ color: inherit;
106
+ }
107
+
108
+ textarea:focus-visible {
109
+ outline: 0;
110
+ border-right: 1px solid #fff9;
111
+ }
112
+
113
+ .fortune-teller-profile p {
114
+ grid-area: bio;
115
+ }
116
+
117
+ .fortune-teller-profile .fortune-teller-avatar {
118
+ display: block;
119
+ aspect-ratio: 1;
120
+ height: 100%;
121
+ border-radius: 50%;
122
+ }
123
+
124
+ .fortune-teller-profile .fortune-teller-deck-back {
125
+ display: none;
126
+ grid-area: deck;
127
+ border-radius: 10%;
128
+ }
129
+
130
+ /*
131
+ .fortune-teller-profile img {
132
+ display: block;
133
+ height: 100%;
134
+ width: auto;
135
+ } */
136
+
137
+ .fortune-teller-buttons {
138
+ display: flex;
139
+ justify-content: center;
140
+ align-items: center;
141
+ }
142
+
143
+ #cards-container {
144
+ position: absolute;
145
+ inset: 0;
146
+ overflow: hidden;
147
+ }
148
+
149
+ .card {
150
+ display: flex;
151
+ justify-content: flex-start;
152
+ align-items: center;
153
+ flex-direction: column-reverse;
154
+ transition: top 0.7s, left 0.7s, rotate 0.5s;
155
+ transition-timing-function: cubic-bezier(0, -0.28, 0.75, -0.21);
156
+ width: 512px;
157
+ height: 768px;
158
+ background-image: linear-gradient(0deg, #000, #fff);
159
+ border-radius: 6%;
160
+ position: absolute;
161
+ transform: translate(-50%, -50%) scale(var(--card-size)) rotateY(0deg);
162
+ border: 3px solid wheat;
163
+ box-shadow: 0 0 10px black;
164
+ top: 50lvh;
165
+ left: 50lvw;
166
+ }
167
+
168
+ .card.rotated {
169
+ transform: translate(-50%, -50%) scale(var(--card-size)) rotateY(90deg);
170
+ }
171
+
172
+ .card.flipped h2 {
173
+ display: none;
174
+ }
175
+
176
+ button {
177
+ cursor: url("./icons/hand.png") 19 4, default;
178
+ width: 4rem;
179
+ height: 4rem;
180
+ background-color: #3336;
181
+ border-radius: 50%;
182
+ border: 0;
183
+ backdrop-filter: blur(10px);
184
+ background-image: none;
185
+ font-size: 2rem;
186
+ color: white;
187
+ background-size: 3rem;
188
+ background-position: center;
189
+ }
190
+
191
+ button.continue-button {
192
+ background-image: url(./icons/play.svg);
193
+ }
194
+
195
+ div.toolbar {
196
+ display: flex;
197
+ align-items: center;
198
+ justify-content: flex-end;
199
+ padding: 1rem 0;
200
+ }
201
+
202
+ #settings-button {
203
+ letter-spacing: 3px;
204
+ font-size: 1rem;
205
+ }
206
+
207
+ #close-settings-button {
208
+ font-size: 1.5rem;
209
+ color: #fff9;
210
+ }
211
+
212
+ div.horizontal-buttons {
213
+ display: flex;
214
+ justify-content: space-between;
215
+ }
216
+
217
+ .card h2 {
218
+ text-align: center;
219
+ background-image: linear-gradient(0deg, #fff 0%, #fff3 140%);
220
+ border-radius: 15%;
221
+ padding: 4px 5px;
222
+ text-shadow: 0 0 3px #333;
223
+ color: #111;
224
+ border: 1px solid #fff6;
225
+ }
226
+
227
+ body.background-01 .card {
228
+ background-image: var(--card-effect), url(./images/deck-01.jpg)
229
+ }
230
+
231
+ body.background-01 .card.flipped {
232
+ background-image: linear-gradient(45deg, #0006, #fff3 70%), url(./images/deck-01-back.png);
233
+
234
+ }
235
+
236
+
237
+ body.background-02 .card {
238
+ background-image: linear-gradient(45deg, #0006, #fff3 70%), url(./images/deck-02.jpg)
239
+ }
240
+
241
+ body.background-02 .card.flipped {
242
+ background-image: linear-gradient(45deg, #0006, #fff3 70%), url('./images/deck-02-back.png');
243
+ }
244
+
245
+
246
+ body.background-03 .card {
247
+ background-image: linear-gradient(45deg, #0006, #fff3 70%), url(./images/deck-03.jpg)
248
+ }
249
+
250
+ body.background-03 .card.flipped {
251
+ background-image: linear-gradient(45deg, #0006, #fff3 70%), url('./images/deck-03-back.png');
252
+ }
253
+
254
+ body.background-05 .card {
255
+ background-image: linear-gradient(45deg, #0006, #fff3 70%), url(./images/deck-05.jpg)
256
+ }
257
+
258
+ body.background-05 .card.flipped {
259
+ background-image: linear-gradient(45deg, #0006, #fff3 70%), url('./images/deck-05-back.png');
260
+ }
261
+
262
+
263
+ body.background-04 .card {
264
+ background-image: url(./images/deck-04.jpg)
265
+ }
266
+
267
+ body.background-04 .card.flipped {
268
+ background-image: url('./images/deck-04-back.png');
269
+ }
270
+
271
+ body.background-06 .card {
272
+ background-image: url(./images/deck-06.jpg)
273
+ }
274
+
275
+ body.background-06 .card.flipped {
276
+ background-image: url('./images/deck-06-back.png');
277
+ }
278
+
279
+
280
+ body.background-07 .card {
281
+ background-image: url(./images/deck-07.jpg)
282
+ }
283
+
284
+ body.background-07 .card.flipped {
285
+ background-image: url('./images/deck-07-back.png');
286
+ }
287
+
288
+ dialog {
289
+ cursor: url("./icons/hand-soft.png") 19 4, default;
290
+ font-size: 1rem;
291
+ backdrop-filter: blur(10px);
292
+ background-image: linear-gradient(0deg, #fff3, transparent);
293
+ border-radius: 2rem;
294
+ border: #fff1 solid 2px;
295
+ background-color: transparent;
296
+ padding: 1rem 2rem 2rem 2rem;
297
+ text-align: center;
298
+ color: white;
299
+ text-shadow: 0 0 5px #fffc;
300
+ }
301
+
302
+ div.tag {
303
+ position: absolute;
304
+ bottom: 10px;
305
+ font-size: 1rem;
306
+ background-image: linear-gradient(45deg, #CDDC39, #8BC34A);
307
+ padding: 0.2rem 1rem;
308
+ border-radius: 1rem;
309
+ font-family: monospace;
310
+ color: #333;
311
+ text-shadow: 0 0 1px #444;
312
+ }
313
+
314
+ div.tag.human {
315
+ background-image: linear-gradient(45deg, #FFEB3B, #ff9800);
316
+ }
317
+
318
+ div.tag.cpu {
319
+ background-image: linear-gradient(45deg, #2196F3, #00BCD4);
320
+ }
321
+
322
+ /* DIALOGS */
323
+ /* Open state of the dialog */
324
+ dialog[open] {
325
+ opacity: 1;
326
+ }
327
+
328
+ /* Closed state of the dialog */
329
+ dialog {
330
+ opacity: 0;
331
+ transition: all 0.7s allow-discrete;
332
+ }
333
+
334
+ /* Before-open state */
335
+ /* Needs to be after the previous dialog[open] rule to take effect,
336
+ as the specificity is the same */
337
+ @starting-style {
338
+ dialog[open] {
339
+ opacity: 0;
340
+ }
341
+ }
342
+
343
+ /* Transition the :backdrop when the dialog modal is promoted to the top layer */
344
+ dialog::backdrop {
345
+ background-color: rgba(0, 0, 0, 0);
346
+ transition: all 0.7s allow-discrete;
347
+ }
348
+
349
+ dialog[open]::backdrop {
350
+ background-color: rgb(0 0 0 / 25%);
351
+ }
352
+
353
+ /* This starting-style rule cannot be nested inside the above selector
354
+ because the nesting selector cannot represent pseudo-elements. */
355
+
356
+ @starting-style {
357
+ dialog[open]::backdrop {
358
+ background-color: rgb(0 0 0 / 0%);
359
+ }
360
+ }
361
+
362
+ dialog div {
363
+ text-align: center;
364
+ color: white;
365
+ }
366
+
367
+
368
+ #settings div {
369
+ text-align: left;
370
+ font-size: 1.3rem;
371
+ }
372
+
373
+ #settings h2 {
374
+ font-weight: 400;
375
+ font-size: 1.2rem;
376
+ }
377
+
378
+ div.setting {
379
+ margin: 2rem 0;
380
+ }
381
+
382
+ div.setting label {
383
+ display: block;
384
+ }
385
+
386
+ div.setting input {
387
+ display: block;
388
+ font-size: 1.3rem;
389
+ width: 42ch;
390
+ background: #fff1;
391
+ color: #cddc39;
392
+ padding: 0.5rem 1rem;
393
+ border: 0;
394
+ font-family: 'Courier New', Courier, monospace;
395
+ }
396
+ div.setting input::placeholder {
397
+
398
+ }
399
+ /* response dialog */
400
+ #response {
401
+ margin-bottom: 1lvh;
402
+ }
403
+
404
+ div.response-container {
405
+ grid-template-columns: 2fr 3fr 1fr;
406
+ align-items: center;
407
+ max-width: 80lvw;
408
+ display: grid;
409
+ }
410
+
411
+ #response-output span {
412
+ display: inline-block;
413
+ }
414
+
415
+ @keyframes bounce {
416
+ 0% {
417
+ transform: translateY(0);
418
+ }
419
+
420
+ 50% {
421
+ transform: translateY(-10px);
422
+ }
423
+
424
+ 100% {
425
+ transform: translateY(0);
426
+ }
427
+ }
428
+
429
+ #response-output span:nth-child(1) {
430
+ animation: bounce 3s infinite 0.5s;
431
+ }
432
+
433
+ #response-output span:nth-child(2) {
434
+ animation: bounce 3s infinite 1.0s;
435
+ }
436
+
437
+ #response-output span:nth-child(3) {
438
+ animation: bounce 3s infinite 1.5s;
439
+ }
440
+
441
+ div.current-fortune-teller {
442
+ aspect-ratio: 1;
443
+ background-size: cover;
444
+ height: 30lvh;
445
+ display: flex;
446
+ margin: auto;
447
+ border-radius: 10%;
448
+ }
449
+
450
+
451
+ body.background-01 div.current-fortune-teller {
452
+ background-image: url(./images/avatar-01.png);
453
+ }
454
+
455
+ body.background-02 div.current-fortune-teller {
456
+ background-image: url(./images/avatar-02.png);
457
+ }
458
+
459
+ body.background-03 div.current-fortune-teller {
460
+ background-image: url(./images/avatar-03.png);
461
+ }
462
+
463
+ body.background-04 div.current-fortune-teller {
464
+ background-image: url(./images/avatar-04.png);
465
+ }
466
+
467
+ body.background-05 div.current-fortune-teller {
468
+ background-image: url(./images/avatar-05.png);
469
+ }
470
+
471
+ body.background-06 div.current-fortune-teller {
472
+ background-image: url(./images/avatar-06.png);
473
+ }
474
+
475
+ body.background-07 div.current-fortune-teller {
476
+ background-image: url(./images/avatar-07.png);
477
+ }
main.js ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Client } from 'https://cdn.jsdelivr.net/npm/@gradio/[email protected]/+esm'
2
+ import MarkdownIt from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm'
3
+
4
+ const md = new MarkdownIt();
5
+
6
+ const majorArcana = [
7
+ "The Fool",
8
+ "The Magician",
9
+ "The High Priestess",
10
+ "The Empress",
11
+ "The Emperor",
12
+ "The Hierophant",
13
+ "The Lovers",
14
+ "The Chariot",
15
+ "Strength",
16
+ "The Hermit",
17
+ "Wheel of Fortune",
18
+ "Justice",
19
+ "The Hanged Man",
20
+ "Death",
21
+ "Temperance",
22
+ "The Devil",
23
+ "The Tower",
24
+ "The Star",
25
+ "The Moon",
26
+ "The Sun",
27
+ "Judgement",
28
+ "The World"
29
+ ];
30
+
31
+ const ask = async function (question, card1, card2, card3) {
32
+ const predictionElement = document.getElementById('response-output');
33
+ const llmSpace = document.getElementById('llm-server').value;
34
+ if (!question) {
35
+ predictionElement.textContent = "Please enter a question first.";
36
+ return;
37
+ }
38
+
39
+ predictionElement.innerHTML = '<span>.</span><span>.</span><span>.</span>';
40
+
41
+ try {
42
+ const client = await Client.connect(llmSpace);
43
+ const result = await client.submit("/chat", {
44
+ message: `${question}`,
45
+ system_prompt: `You are multilingual tarot card reader and fortune teller. You picked from your Tarot Deck **${majorArcana[card1]}**, **${majorArcana[card2]}**, **${majorArcana[card3]}**. Answer to the question using same language and format using Markdown.`,
46
+ max_new_tokens: 288,
47
+ // temperature: 0.7,
48
+ // top_p: 0.95,
49
+ // top_k: 50,
50
+ // repetition_penalty: 1,
51
+ });
52
+
53
+ let fullResponse = '';
54
+ for await (const msg of result) {
55
+ if (msg.type === "data") {
56
+ fullResponse = msg.data[0];
57
+ predictionElement.innerHTML = md.render(fullResponse);
58
+ } else {
59
+ console.log(msg)
60
+ }
61
+ }
62
+
63
+ } catch (error) {
64
+ console.error('Error:', error);
65
+ predictionElement.textContent = "The cards are clouded. Please try again later.";
66
+ }
67
+ }
68
+
69
+ document.addEventListener('DOMContentLoaded', () => {
70
+
71
+ const welcomeDialog = document.getElementById('welcome');
72
+ const deckSelectionDialog = document.getElementById('deck-selection');
73
+ const questionDialog = document.getElementById('question');
74
+ const responseDialog = document.getElementById('response');
75
+ const cardsContainer = document.getElementById("cards-container");
76
+ const settingsDialog = document.getElementById('settings');
77
+
78
+ // Funzione per aprire il dialogo di benvenuto
79
+ function openWelcomeDialog() {
80
+ welcomeDialog.showModal();
81
+ }
82
+
83
+ // Funzione per aprire il dialogo di selezione del mazzo
84
+ function openDeckSelectionDialog() {
85
+ welcomeDialog.close(); // Chiudi il dialogo di benvenuto
86
+ deckSelectionDialog.showModal(); // Apri il dialogo di selezione del mazzo
87
+ }
88
+
89
+ const fortuneTellerProfiles = document.querySelectorAll('.fortune-teller-profile');
90
+ fortuneTellerProfiles.forEach(profile => {
91
+ const fortuneTellerId = profile.getAttribute('data-fortune-teller-id');
92
+
93
+
94
+ profile.addEventListener('click', (event) => {
95
+
96
+ changeBackgroundImage(fortuneTellerId); // Cambia il background-image di body
97
+
98
+ });
99
+ });
100
+ const CARDS_WIDTH = 512;
101
+ const CARDS_HEIGHT = 768;
102
+ const CARDS_NUMERALS = ['0', 'I', 'II', 'III', 'IV', '', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX', 'XXI', 'XXII'];
103
+
104
+ function resetDeck() {
105
+
106
+ cardsContainer.innerHTML = ''; // Rimuove le card esistenti
107
+ for (let i = 0; i < 22; i++) {
108
+ const card = document.createElement("div");
109
+ card.classList.add("card");
110
+ card.id = `card-${i}`;
111
+ card.innerHTML = `<h2>${CARDS_NUMERALS[i]}</h2>`;
112
+ const randomX = parseInt(1 + Math.random() * 8) * 10;
113
+ const randomY = parseInt(3 + Math.random() * 4) * 10;
114
+ card.style.top = randomY + "lvh";
115
+ card.style.left = randomX + "lvw";
116
+ card.style.backgroundPositionX = -(parseInt(i % 11) * CARDS_WIDTH) + "px";
117
+ card.style.backgroundPositionY = -(parseInt(i / 11) * CARDS_HEIGHT) + "px";
118
+ cardsContainer.appendChild(card);
119
+ }
120
+ }
121
+ // Funzione per cambiare il background-image di body in base all'ID del cartomante
122
+ function changeBackgroundImage(fortuneTellerId) {
123
+ selectedButton.removeAttribute('disabled');
124
+ const oldSelectedItem = document.querySelector('.fortune-teller-profile.selected');
125
+ if (oldSelectedItem && oldSelectedItem.getAttribute('data-fortune-teller-id') == fortuneTellerId) {
126
+ return;
127
+ }
128
+ resetDeck()
129
+ document.body.classList.remove('background-01', 'background-02', 'background-03', 'background-04', 'background-05', 'background-06', 'background-07');
130
+ const fortuneTellerClass = 'background-' + fortuneTellerId;
131
+ document.body.classList.add(fortuneTellerClass);
132
+
133
+ const newSelectedItem = document.querySelector(`[data-fortune-teller-id="${fortuneTellerId}"]`)
134
+ if (oldSelectedItem) {
135
+ oldSelectedItem.classList.remove('selected');
136
+ }
137
+
138
+ newSelectedItem.classList.add('selected');
139
+ }
140
+
141
+ const openSettingsDialog = () => {
142
+ settingsDialog.showModal();
143
+ }
144
+ const closeSettingsDialog = () => {
145
+ settingsDialog.close();
146
+ }
147
+ const startButton = document.getElementById('start');
148
+ startButton.addEventListener('click', openDeckSelectionDialog);
149
+ const settingsButton = document.getElementById('settings-button');
150
+ settingsButton.addEventListener('click', openSettingsDialog);
151
+ const closeSettingsButton = document.getElementById('close-settings-button');
152
+ closeSettingsButton.addEventListener('click', closeSettingsDialog);
153
+
154
+ const selectedButton = document.getElementById('fortune-teller-selected');
155
+ selectedButton.addEventListener('click', () => {
156
+ deckSelectionDialog.close(); // Chiudi il dialogo di selezione del mazzo
157
+ questionDialog.showModal(); // Apri il dialogo delle domande
158
+ });
159
+
160
+ function recollectCards(positionX = 50, positionY = 50) {
161
+ return new Promise((resolve, reject) => {
162
+ const deck = Array.from(document.querySelectorAll('.card'));
163
+ const animationDuration = 1000; // Durata dell'animazione in millisecondi
164
+
165
+ deck.forEach((card, index) => {
166
+ // card.style.transition = `top ${animationDuration}ms ease-out, left ${animationDuration}ms ease-out`;
167
+ card.classList.add('flipped');
168
+ card.style.top = positionY + 'lvh';
169
+ card.style.left = positionX + 'lvw';
170
+ // Utilizziamo l'ultimo elemento nell'array per individuare la fine dell'animazione
171
+ if (index === deck.length - 1) {
172
+ card.addEventListener('transitionend', () => {
173
+ resolve(); // Risolviamo la Promise quando l'ultima carta ha terminato l'animazione
174
+
175
+ }, { once: true });
176
+ }
177
+ });
178
+ });
179
+ }
180
+
181
+ function shuffleCards() {
182
+ const cardsContainer = document.getElementById('cards-container');
183
+ const cards = cardsContainer.querySelectorAll('.card');
184
+
185
+ // Convertiamo NodeList in un array per poterlo manipolare facilmente
186
+ const cardsArray = Array.from(cards);
187
+
188
+ // Usiamo l'algoritmo di Fisher-Yates per mescolare l'array
189
+ for (let i = cardsArray.length - 1; i > 0; i--) {
190
+ const j = Math.floor(Math.random() * (i + 1));
191
+ [cardsArray[i], cardsArray[j]] = [cardsArray[j], cardsArray[i]];
192
+ }
193
+
194
+ // Rimuoviamo le carte esistenti dal container
195
+ cardsContainer.innerHTML = '';
196
+
197
+ // Aggiungiamo le carte mescolate nuovamente al container
198
+ cardsArray.forEach(card => {
199
+
200
+ cardsContainer.appendChild(card);
201
+ card.style.top = '50lvh';
202
+ card.style.left = '50lvw';
203
+ });
204
+ }
205
+
206
+
207
+ const questionButton = document.getElementById('questionButton');
208
+ questionButton.addEventListener('click', () => {
209
+
210
+ recollectCards().then(() => {
211
+ shuffleCards();
212
+ const animation = () => {
213
+ recollectCards(100, 10).then(() => {
214
+
215
+ setTimeout(() => {
216
+ const cardsContainer = document.getElementById('cards-container');
217
+ const cards = Array.from(cardsContainer.querySelectorAll('.card'));
218
+
219
+
220
+ const card1 = cards[21];
221
+
222
+
223
+ card1.addEventListener('transitionend', () => {
224
+ card1.classList.remove('flipped')
225
+
226
+ }, { once: true });
227
+
228
+ card1.style.top = '30lvh';
229
+ card1.style.left = '30lvw';
230
+
231
+
232
+
233
+ const card2 = cards[20];
234
+
235
+ card2.addEventListener('transitionend', () => {
236
+ card2.classList.remove('flipped')
237
+ card2.style.transitionDelay = '0s';
238
+ }, { once: true });
239
+ card2.style.transitionDelay = '0.6s'
240
+ card2.style.top = '30lvh';
241
+ card2.style.left = '50lvw';
242
+
243
+ const card3 = cards[19];
244
+
245
+ card3.addEventListener('transitionend', () => {
246
+ card3.classList.remove('flipped')
247
+ card3.style.transitionDelay = '0s';
248
+
249
+ setTimeout(() => {
250
+ responseDialog.showModal();
251
+ }, 1000)
252
+
253
+ }, { once: true });
254
+ card3.style.top = '30lvh';
255
+ card3.style.left = '70lvw';
256
+ card3.style.transitionDelay = '1.2s'
257
+
258
+ const cardID1 = parseInt(card1.getAttribute('id').replace('card-', ''));
259
+ const cardID2 = parseInt(card2.getAttribute('id').replace('card-', ''))
260
+ const cardID3 = parseInt(card3.getAttribute('id').replace('card-', ''))
261
+ const question = document.getElementById('questionInput').value;
262
+ ask(question, cardID1, cardID2, cardID3);
263
+
264
+ }, 600);
265
+ })
266
+
267
+ }
268
+ setTimeout(animation, 600);
269
+ })
270
+
271
+ //deckSelectionDialog.close(); // Chiudi il dialogo di selezione del mazzo
272
+ questionDialog.close(); // Apri il dialogo delle domande
273
+ });
274
+
275
+
276
+
277
+ const newQuestionButton = document.getElementById('new-question');
278
+ newQuestionButton.addEventListener('click', () => {
279
+ responseDialog.close();
280
+ const responseOutputElement = document.getElementById('response-output');
281
+ document.getElementById('questionInput').value = ''
282
+ responseOutputElement.innerHTML = '<span>.</span><span>.</span><span>.</span>'
283
+ questionDialog.showModal();
284
+ })
285
+
286
+ const changeFortuneTellerButton = document.getElementById('changeFortuneTellerButton');
287
+ changeFortuneTellerButton.addEventListener('click', () => {
288
+ questionDialog.close();
289
+ openDeckSelectionDialog();
290
+ })
291
+
292
+ // Chiamata alla funzione per aprire il dialogo di benvenuto all'avvio dell'app
293
+ openDeckSelectionDialog();
294
+ });