feeds-fun 1.16.6 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/package.json +1 -1
  2. package/src/components/DiscoveryForm.vue +3 -5
  3. package/src/components/EntriesList.vue +1 -11
  4. package/src/components/EntryForList.vue +54 -67
  5. package/src/components/FeedForList.vue +29 -64
  6. package/src/components/FeedInfo.vue +12 -14
  7. package/src/components/FeedsList.vue +2 -1
  8. package/src/components/OPMLUpload.vue +3 -3
  9. package/src/components/RuleConstructor.vue +12 -6
  10. package/src/components/RuleForList.vue +25 -24
  11. package/src/components/RulesList.vue +2 -1
  12. package/src/components/SimplePagination.vue +2 -2
  13. package/src/components/SupertokensLogin.vue +5 -6
  14. package/src/components/UserSetting.vue +7 -12
  15. package/src/components/UserSettingForNotification.vue +4 -3
  16. package/src/components/body_list/EntryBody.vue +43 -0
  17. package/src/components/body_list/FaviconColumn.vue +24 -0
  18. package/src/components/body_list/ReverseTimeColumn.vue +28 -0
  19. package/src/components/collections/Block.vue +1 -1
  20. package/src/components/collections/BlockItem.vue +4 -3
  21. package/src/components/collections/DetailedItem.vue +4 -10
  22. package/src/components/collections/FeedItem.vue +31 -24
  23. package/src/components/collections/Notification.vue +6 -8
  24. package/src/components/collections/SubscribingProgress.vue +14 -17
  25. package/src/components/collections/Warning.vue +36 -38
  26. package/src/components/main/Block.vue +5 -0
  27. package/src/components/main/Description.vue +51 -0
  28. package/src/components/main/HeaderLine.vue +7 -0
  29. package/src/components/main/Item.vue +27 -0
  30. package/src/components/main/NewsTitle.vue +26 -0
  31. package/src/components/notifications/ApiKey.vue +14 -5
  32. package/src/components/notifications/CreateRuleHelp.vue +10 -11
  33. package/src/components/page_header/ExternalLinks.vue +58 -0
  34. package/src/components/tags/Base.vue +28 -0
  35. package/src/components/tags/EntryTag.vue +73 -0
  36. package/src/components/{TagsList.vue → tags/EntryTagsList.vue} +8 -10
  37. package/src/components/tags/FakeTag.vue +35 -0
  38. package/src/components/{FfunTag.vue → tags/FilterTag.vue} +36 -52
  39. package/src/components/tags/RuleTag.vue +67 -0
  40. package/src/components/{TagsFilter.vue → tags/TagsFilter.vue} +12 -8
  41. package/src/css/base.css +38 -0
  42. package/src/css/inputs.css +49 -0
  43. package/src/css/page_header.css +34 -0
  44. package/src/css/panels.css +49 -0
  45. package/src/css/side_panel_layout.css +34 -0
  46. package/src/css/tags.css +44 -0
  47. package/src/layouts/SidePanelLayout.vue +35 -91
  48. package/src/layouts/WideLayout.vue +5 -17
  49. package/src/logic/events.ts +1 -1
  50. package/src/logic/types.ts +16 -7
  51. package/src/logic/utils.ts +29 -0
  52. package/src/main.ts +42 -10
  53. package/src/style.css +10 -100
  54. package/src/values/DateTime.vue +1 -1
  55. package/src/values/{URL.vue → ExternalUrl.vue} +7 -6
  56. package/src/views/CollectionsView.vue +3 -6
  57. package/src/views/DiscoveryView.vue +9 -11
  58. package/src/views/FeedsView.vue +3 -2
  59. package/src/views/MainView.vue +189 -44
  60. package/src/views/NewsView.vue +2 -1
  61. package/src/views/RulesView.vue +6 -3
  62. package/src/views/SettingsView.vue +92 -33
  63. package/src/inputs/Marker.vue +0 -54
@@ -1,9 +1,10 @@
1
1
  <template>
2
- <button
2
+ <a
3
3
  v-if="setting"
4
+ href=""
4
5
  @click.prevent="updateFlag(true)"
5
- class="ffun-normal-link"
6
- >{{ text }}</button
6
+ class=""
7
+ >{{ text }}</a
7
8
  >
8
9
  </template>
9
10
 
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <div class="flex my-1">
3
+ <div class="max-w-3xl flex-1 bg-white border rounded p-4">
4
+ <h2
5
+ v-if="url"
6
+ class="mt-0"
7
+ ><a
8
+ :href="url"
9
+ target="_blank"
10
+ @click="emit('body-title-clicked')"
11
+ >{{ title }}</a
12
+ ></h2
13
+ >
14
+ <p v-if="loading">loading…</p>
15
+ <div
16
+ v-if="text"
17
+ class="prose max-w-none"
18
+ v-html="text" />
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <script lang="ts" setup>
24
+ import _ from "lodash";
25
+ import {computed, ref, useTemplateRef, onMounted} from "vue";
26
+ import type * as t from "@/logic/types";
27
+ import * as events from "@/logic/events";
28
+ import * as e from "@/logic/enums";
29
+ import {computedAsync} from "@vueuse/core";
30
+ import DOMPurify from "dompurify";
31
+ import {useEntriesStore} from "@/stores/entries";
32
+
33
+ const entriesStore = useEntriesStore();
34
+
35
+ const properties = defineProps<{
36
+ url: string | null;
37
+ title: string | null;
38
+ loading: boolean;
39
+ text: string | null;
40
+ }>();
41
+
42
+ const emit = defineEmits(["body-title-clicked"]);
43
+ </script>
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <div class="flex-shrink-0 w-8 text-right pr-1">
3
+ <favicon-element
4
+ :url="url"
5
+ class="w-5 h-5 mx-1 inline align-text-bottom" />
6
+ </div>
7
+ </template>
8
+
9
+ <script lang="ts" setup>
10
+ import _ from "lodash";
11
+ import {computed, ref, useTemplateRef, onMounted} from "vue";
12
+ import type * as t from "@/logic/types";
13
+ import * as events from "@/logic/events";
14
+ import * as e from "@/logic/enums";
15
+ import {computedAsync} from "@vueuse/core";
16
+ import DOMPurify from "dompurify";
17
+ import {useEntriesStore} from "@/stores/entries";
18
+
19
+ const entriesStore = useEntriesStore();
20
+
21
+ const properties = defineProps<{
22
+ url: string;
23
+ }>();
24
+ </script>
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <div
3
+ class="flex-shrink-0 w-16 cursor-default text-right"
4
+ :title="title">
5
+ <value-date-time
6
+ class=""
7
+ :value="time"
8
+ :reversed="true" />
9
+ </div>
10
+ </template>
11
+
12
+ <script lang="ts" setup>
13
+ import _ from "lodash";
14
+ import {computed, ref, useTemplateRef, onMounted} from "vue";
15
+ import type * as t from "@/logic/types";
16
+ import * as events from "@/logic/events";
17
+ import * as e from "@/logic/enums";
18
+ import {computedAsync} from "@vueuse/core";
19
+ import DOMPurify from "dompurify";
20
+ import {useEntriesStore} from "@/stores/entries";
21
+
22
+ const entriesStore = useEntriesStore();
23
+
24
+ const properties = defineProps<{
25
+ time: Date | null;
26
+ title: string;
27
+ }>();
28
+ </script>
@@ -9,7 +9,7 @@
9
9
 
10
10
  <button
11
11
  type="submit"
12
- class="ffun-form-button"
12
+ class="ffun-form-button mt-4"
13
13
  >Subscribe</button
14
14
  >
15
15
 
@@ -9,11 +9,12 @@
9
9
  v-model="model"
10
10
  checked />
11
11
  <label
12
- class="ml-2"
12
+ class="ml-2 inline-block"
13
13
  :for="collection.id">
14
14
  <div class="inline-block">
15
- <span class="text-green-700 font-bold">{{ collection.name }}</span> [feeds: {{ collection.feedsNumber }}]
16
- <p class="">{{ collection.description }}</p>
15
+ <span class="text-green-700 font-bold">{{ collection.name }}</span>
16
+ {{ collection.feedsNumber }} feeds —
17
+ {{ collection.description }}
17
18
  </div>
18
19
  </label>
19
20
  </div>
@@ -4,12 +4,12 @@
4
4
  <p class="">{{ collection.description }}</p>
5
5
 
6
6
  <div v-if="showFeeds">
7
- <div
7
+ <ul
8
8
  v-for="feed in feeds"
9
9
  :key="feed.url"
10
- class="mb-2 pb-2 collection-feed-block">
10
+ class="ffun-body-list-entry">
11
11
  <collections-feed-item :feed="feed" />
12
- </div>
12
+ </ul>
13
13
  </div>
14
14
 
15
15
  <button
@@ -17,7 +17,7 @@
17
17
  class="ffun-form-button mr-2">
18
18
  <template v-if="collection.feedsNumber === 1"> Subscribe to 1 feed </template>
19
19
 
20
- <template v-else> Subscribe to all {{ collection.feedsNumber }} feeds </template>
20
+ <template v-else> Subscribe to {{ collection.feedsNumber }} feeds </template>
21
21
  </button>
22
22
 
23
23
  <button
@@ -107,9 +107,3 @@
107
107
  {lazy: true}
108
108
  );
109
109
  </script>
110
-
111
- <style scoped>
112
- .collection-feed-block:not(:last-child) {
113
- border-bottom-width: 1px;
114
- }
115
- </style>
@@ -1,39 +1,37 @@
1
1
  <template>
2
- <div class="flex">
3
- <div class="flex-shrink-0 w-16 text-right pr-1">
2
+ <div class="flex text-lg">
3
+ <div class="ffun-body-list-icon-column">
4
4
  <a
5
+ v-if="subscribed"
5
6
  href="#"
6
- v-if="!subscribed"
7
- @click.prevent.stop="feedsStore.subscribe(feed.url)"
8
- class="text-blue-700"
9
- >subscribe</a
10
- >
7
+ @click.prevent="feedsStore.unsubscribe(feed.id)"
8
+ title="Unsubscribe from this feed"
9
+ class="text-red-500 hover:text-red-600 ti ti-x" />
11
10
 
12
- <span
11
+ <a
13
12
  v-else
14
- class="text-green-700 cursor-default"
15
- >subscribed</span
16
- >
13
+ href="#"
14
+ @click.prevent="feedsStore.subscribe(feed.url)"
15
+ title="Subscribe to this feed"
16
+ class="text-green-600 hover:text-green-700 ti ti-plus" />
17
17
  </div>
18
18
 
19
- <div class="flex-shrink-0 w-8 text-right pr-1">
20
- <favicon-element
21
- :url="feed.url"
22
- class="w-4 h-4 align-text-bottom mx-1 inline" />
23
- </div>
19
+ <body-list-favicon-column :url="feed.url" />
24
20
 
25
21
  <div class="flex-grow">
26
- <strong>{{ feed.title }}</strong>
22
+ <external-url
23
+ class="ffun-normal-link"
24
+ :url="feed.url"
25
+ :text="purifiedTitle" />
27
26
  </div>
28
27
  </div>
29
28
 
30
- <div class="flex">
31
- <div class="flex-shrink-0 w-16 pr-1"> </div>
32
-
33
- <div class="max-w-3xl flex-1 bg-slate-50 border-2 rounded p-4">
34
- <p>{{ feed.description }}</p>
35
- </div>
36
- </div>
29
+ <body-list-entry-body
30
+ class="ml-8"
31
+ :url="null"
32
+ :title="null"
33
+ :loading="false"
34
+ :text="purifiedDescription" />
37
35
  </template>
38
36
 
39
37
  <script lang="ts" setup>
@@ -41,6 +39,7 @@
41
39
  import type * as t from "@/logic/types";
42
40
  import * as e from "@/logic/enums";
43
41
  import * as api from "@/logic/api";
42
+ import * as utils from "@/logic/utils";
44
43
  import {computedAsync} from "@vueuse/core";
45
44
  import DOMPurify from "dompurify";
46
45
  import {useEntriesStore} from "@/stores/entries";
@@ -57,6 +56,14 @@
57
56
  const globalSettings = useGlobalSettingsStore();
58
57
 
59
58
  const subscribed = computed(() => properties.feed.id in feedsStore.feeds);
59
+
60
+ const purifiedTitle = computed(() => {
61
+ return utils.purifyTitle({raw: properties.feed.title, default_: properties.feed.url});
62
+ });
63
+
64
+ const purifiedDescription = computed(() => {
65
+ return utils.purifyBody({raw: properties.feed.description, default_: "No description"});
66
+ });
60
67
  </script>
61
68
 
62
69
  <style scoped></style>
@@ -1,11 +1,9 @@
1
1
  <template>
2
- <div class="ffun-info-attention">
3
- <h3>It looks like there's nothing to read!</h3>
4
- <p
5
- >But don't worry we've prepared some ready-to-use thematic collections just for you. All the feeds in them are
6
- fully tagged free of charge.</p
7
- >
8
- <p>Subscribe to some and enjoy the full power of Feeds Fun!</p>
9
- <collections-block />
2
+ <div class="ffun-info-common">
3
+ <h4>Subscribe to curated collections</h4>
4
+ <p>We've prepared thematic collections just for you.</p>
5
+ <p>News from collections are always tagged, ensuring you get the full power of Feeds Fun!</p>
6
+
7
+ <collections-block class="mt-4" />
10
8
  </div>
11
9
  </template>
@@ -1,22 +1,19 @@
1
1
  <template>
2
- <div>
3
- <p
4
- v-if="loading"
5
- class="ffun-info-attention"
6
- >Subscribing...</p
7
- >
8
-
9
- <p
10
- v-if="loaded"
11
- class="ffun-info-good"
12
- >Feeds added!</p
13
- >
2
+ <div
3
+ v-if="loading"
4
+ class="ffun-info-waiting mt-4">
5
+ <p>Subscribing…</p>
6
+ </div>
14
7
 
15
- <p
16
- v-if="error"
17
- class="ffun-info-bad"
18
- >Unknown error occurred! Please, try later.</p
19
- >
8
+ <div
9
+ v-if="loaded"
10
+ class="ffun-info-good mt-4"
11
+ ><p>Feeds added!</p>
12
+ </div>
13
+ <div
14
+ v-if="error"
15
+ class="ffun-info-bad mt-4"
16
+ ><p>Unknown error occurred! Please, try later.</p>
20
17
  </div>
21
18
  </template>
22
19
 
@@ -1,54 +1,52 @@
1
1
  <template>
2
- <div class="ffun-info-attention">
3
- <h3>Check your feed URLs</h3>
2
+ <div class="ffun-info-common">
3
+ <h4>Pay attention to your feed URLs</h4>
4
+
5
+ <p>
6
+ Some websites, like
7
+ <external-url
8
+ url="https://www.reddit.com"
9
+ text="Reddit" />
4
10
 
5
- <p
6
- >Websites like
7
- <a
8
- href="https://reddit.com"
9
- target="_blank"
10
- >Reddit</a
11
- >
12
11
  or
13
- <a
14
- href="https://arxiv.org"
15
- target="_blank"
16
- >arXiv</a
17
- >
18
- provide multiple feeds by:</p
19
- >
12
+ <external-url
13
+ url="https://arxiv.org"
14
+ text="arXiv" />, provide multiple URLs for feeds:
15
+ </p>
20
16
 
21
17
  <ul>
22
- <li
23
- >Customizing news categories lists in URL (like
24
- <code class="inline-block">https://site.com/feed/rss?category1+category2</code>) instead of using separate
25
- URLs.</li
26
- >
27
- <li
28
- >Supporting different feed formats (like <code class="inline-block">https://site.com/feed/rss</code> and
29
- <code class="inline-block">https://site.com/feed/atom</code>).</li
30
- >
18
+ <li>
19
+ By customizing the list of categories, like
20
+ <code class="inline-block">https://example.org/feed/rss?category1+category2</code>.
21
+ </li>
22
+ <li>
23
+ By supporting different feed formats, like
24
+ <code class="inline-block">https://example.org/feed/rss</code> and
25
+ <code class="inline-block">https://example.org/feed/atom</code>.
26
+ </li>
31
27
  </ul>
32
28
 
33
- <p
34
- >This can lead to the same news item appearing in multiple feeds, not being recognized as news from collections,
35
- and being processed with your API key. We do our best to detect such news, but there are always exceptions.</p
36
- >
29
+ <p> Consequently, the same news may appear in multiple feeds. </p>
30
+
31
+ <p>
32
+ We do our best to detect duplicates, but in some cases, they may slip through and lead to extra usage of your API
33
+ key.
34
+ </p>
37
35
 
38
- <p><strong>To get the most from free tagging in collections:</strong></p>
36
+ <p><strong>Please follow these recommendations:</strong></p>
39
37
 
40
38
  <ul>
41
- <li>Use specific, granular feed URLs.</li>
42
- <li
43
- >Add feeds directly from
39
+ <li>Use short, granular feed URLs.</li>
40
+ <li>If a feed URL can be split into multiple simpler URLs, add them separately.</li>
41
+ <li>
42
+ Add feeds directly from
44
43
  <a
45
- class="ffun-normal-link"
46
44
  href="#"
47
- @click.prevent="goToCollections()"
48
- >collections</a
45
+ @click.prevent="goToCollections()">
46
+ collections</a
49
47
  >
50
- instead of using URLs from sites.</li
51
- >
48
+ instead of using URLs from websites.
49
+ </li>
52
50
  </ul>
53
51
 
54
52
  <user-setting-for-notification
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <div class="text-center max-w-3xl mx-auto my-3 md:my-5 space-y-3 md:space-y-5">
3
+ <slot></slot>
4
+ </div>
5
+ </template>
@@ -0,0 +1,51 @@
1
+ <template>
2
+ <!-- PC -->
3
+ <div class="hidden md:flex items-stretch rounded-xl overflow-hidden min-h-28 max-h-28 mx-2">
4
+ <div class="w-12 bg-blue-200 flex items-center justify-center text-4xl font-bold">
5
+ <i :class="['ti', icon]"></i>
6
+ </div>
7
+
8
+ <div class="w-1/3 bg-blue-100 px-4 flex items-center text-xl font-medium">
9
+ <slot name="caption" />
10
+ </div>
11
+
12
+ <div class="flex-1 bg-slate-100 p-6 text-gray-800 flex items-center">
13
+ <div class="w-full">
14
+ <slot name="description" />
15
+ </div>
16
+ </div>
17
+ </div>
18
+
19
+ <!-- Mobile -->
20
+ <div class="flex flex-col md:hidden flex items-stretch rounded-xl overflow-hidden min-h-28 max-h-28 mx-2">
21
+ <div class="w-full flex">
22
+ <div class="w-12 bg-blue-200 flex items-center justify-center text-lg font-bold">
23
+ <i :class="['ti', icon]"></i>
24
+ </div>
25
+
26
+ <div class="flex-grow bg-blue-100 px-4 flex justify-center text-lg font-medium">
27
+ <slot name="caption" />
28
+ </div>
29
+ </div>
30
+
31
+ <div class="flex-1 bg-slate-100 px-4 text-gray-800 flex items-center">
32
+ <div class="w-full">
33
+ <slot name="description" />
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </template>
38
+
39
+ <script lang="ts" setup>
40
+ import * as t from "@/logic/types";
41
+ import {computed, ref, inject} from "vue";
42
+ import type {Ref} from "vue";
43
+ import {useTagsStore} from "@/stores/tags";
44
+ import * as tagsFilterState from "@/logic/tagsFilterState";
45
+ import * as asserts from "@/logic/asserts";
46
+ import * as events from "@/logic/events";
47
+
48
+ const properties = defineProps<{
49
+ icon: string;
50
+ }>();
51
+ </script>
@@ -0,0 +1,7 @@
1
+ <template>
2
+ <div class="bg-slate-200 text-center p-3 md:py-5 border border-x-0 border-slate-400">
3
+ <p class="text-2xl md:text-3xl font-medium">
4
+ <slot></slot>
5
+ </p>
6
+ </div>
7
+ </template>
@@ -0,0 +1,27 @@
1
+ <template>
2
+ <div class="flex flex-col items-stretch rounded-xl overflow-hidden min-h-28 max-h-28 mx-2">
3
+ <div class="bg-blue-100 py-1 items-center text-xl font-medium">
4
+ <slot name="caption" />
5
+ </div>
6
+
7
+ <div class="flex-1 bg-slate-100 p-2 text-gray-800 flex items-center">
8
+ <div class="">
9
+ <slot name="description" />
10
+ </div>
11
+ </div>
12
+ </div>
13
+ </template>
14
+
15
+ <script lang="ts" setup>
16
+ import * as t from "@/logic/types";
17
+ import {computed, ref, inject} from "vue";
18
+ import type {Ref} from "vue";
19
+ import {useTagsStore} from "@/stores/tags";
20
+ import * as tagsFilterState from "@/logic/tagsFilterState";
21
+ import * as asserts from "@/logic/asserts";
22
+ import * as events from "@/logic/events";
23
+
24
+ const properties = defineProps<{
25
+ icon: string;
26
+ }>();
27
+ </script>
@@ -0,0 +1,26 @@
1
+ <template>
2
+ <span class="block text-base md:text-lg">
3
+ <span
4
+ v-if="score"
5
+ class="cursor-pointer text-purple-700 inline-block w-12"
6
+ >{{ score }}</span
7
+ >
8
+
9
+ <span class="font-bold">{{ title }}</span>
10
+ </span>
11
+ </template>
12
+
13
+ <script lang="ts" setup>
14
+ import * as t from "@/logic/types";
15
+ import {computed, ref, inject} from "vue";
16
+ import type {Ref} from "vue";
17
+ import {useTagsStore} from "@/stores/tags";
18
+ import * as tagsFilterState from "@/logic/tagsFilterState";
19
+ import * as asserts from "@/logic/asserts";
20
+ import * as events from "@/logic/events";
21
+
22
+ const properties = defineProps<{
23
+ title: string;
24
+ score: number | null;
25
+ }>();
26
+ </script>
@@ -1,5 +1,7 @@
1
1
  <template>
2
- <div class="ffun-info-attention">
2
+ <div class="ffun-info-common">
3
+ <h4>Setup API keys</h4>
4
+
3
5
  <p>
4
6
  Please
5
7
  <a
@@ -7,17 +9,24 @@
7
9
  @click.prevent="goToSettings()"
8
10
  >set up</a
9
11
  >
10
- your own OpenAI and/or Gemini API keys to tag your personal feeds.
12
+ your own
13
+ <external-url
14
+ url="https://platform.openai.com/docs/api-reference/introduction"
15
+ text="OpenAI" />
16
+ or
17
+ <external-url
18
+ url="https://ai.google.dev/gemini-api/docs/api-key"
19
+ text="Gemini" />
20
+ API keys to tag your personal feeds.
11
21
  </p>
12
22
 
13
23
  <p>
14
- Remember, Feeds Fun provides tags for feeds from
15
24
  <a
16
25
  href="#"
17
26
  @click.prevent="goToCollections()"
18
- >collections</a
27
+ >Collection</a
19
28
  >
20
- for free. You do not need to set up your own API keys to have tags in news from collections.
29
+ feeds are tagged automatically, so no actions are required for them.
21
30
  </p>
22
31
 
23
32
  <user-setting-for-notification
@@ -1,20 +1,19 @@
1
1
  <template>
2
- <div class="ffun-info-attention">
3
- <h3>Create your first news score rule</h3>
4
- <p>Set up a rule to filter your news:</p>
2
+ <div class="ffun-info-common">
3
+ <h4>Create your first rule</h4>
4
+
5
5
  <ol class="list-decimal list-inside">
6
- <li><strong>Click</strong> any tag on a news item.</li>
7
- <li>See how news is filtered by the chosen tag.</li>
8
- <li>Locate the <strong>Create Rule</strong> panel in the left sidebar.</li>
6
+ <li><strong>Click on a tag</strong> that interests you.</li>
7
+ <li>See how Feeds Fun filters news based on the selected tag.</li>
9
8
  <li>
10
- Assign a numeric score for filtered news:
9
+ <strong>Assign a score</strong> to the filtered news in the "Create Rule" block in the left sidebar:
11
10
  <ul>
12
- <li>Use higher scores (above zero) for important or engaging content.</li>
13
- <li>Use lower (below zero) scores for uninteresting or irrelevant content.</li>
11
+ <li>Use positive scores for important or engaging content.</li>
12
+ <li>Use negative scores for irrelevant or boring content.</li>
14
13
  </ul>
15
14
  </li>
16
- <li>Click <strong>Create Rule</strong>.</li>
17
- <li>Watch the news reorganize based on your scores.</li>
15
+ <li><strong>Click the "Create Rule" button</strong>.</li>
16
+ <li>See how Feeds Fun reorganizes news based on your rule.</li>
18
17
  </ol>
19
18
  </div>
20
19
  </template>
@@ -0,0 +1,58 @@
1
+ <template>
2
+ <a
3
+ v-if="showApi"
4
+ href="/api/docs"
5
+ target="_blank"
6
+ class="ffun-page-header-link"
7
+ @click="events.socialLinkClicked({linkType: 'api'})"
8
+ >API</a
9
+ >
10
+
11
+ <a
12
+ v-if="settings.blog"
13
+ :href="settings.blog"
14
+ target="_blank"
15
+ class="ffun-page-header-link"
16
+ @click="events.socialLinkClicked({linkType: 'blog'})"
17
+ >Blog</a
18
+ >
19
+
20
+ <a
21
+ v-if="settings.redditSubreddit"
22
+ :href="settings.redditSubreddit"
23
+ target="_blank"
24
+ class="ffun-page-header-link text-xl align-middle"
25
+ title="Reddit"
26
+ @click="events.socialLinkClicked({linkType: 'reddit'})"
27
+ ><i class="ti ti-brand-reddit"></i
28
+ ></a>
29
+
30
+ <a
31
+ v-if="settings.discordInvite"
32
+ :href="settings.discordInvite"
33
+ target="_blank"
34
+ class="ffun-page-header-link text-xl align-middle"
35
+ title="Discord"
36
+ @click="events.socialLinkClicked({linkType: 'discord'})"
37
+ ><i class="ti ti-brand-discord"></i
38
+ ></a>
39
+
40
+ <a
41
+ v-if="settings.githubRepo"
42
+ :href="settings.githubRepo"
43
+ target="_blank"
44
+ class="ffun-page-header-link text-xl align-middle"
45
+ title="GitHub"
46
+ @click="events.socialLinkClicked({linkType: 'github'})">
47
+ <i class="ti ti-brand-github"></i
48
+ ></a>
49
+ </template>
50
+
51
+ <script lang="ts" setup>
52
+ import {ref, computed, useSlots, onMounted, watch, watchEffect} from "vue";
53
+
54
+ import * as events from "@/logic/events";
55
+ import * as settings from "@/logic/settings";
56
+
57
+ const properties = defineProps<{showApi: boolean}>();
58
+ </script>