דילוג לתוכן
0
  • דף הבית
  • חוקי הפורום
  • מדריכים
  • פוסטים אחרונים
  • לא נפתר
  • תגיות
  • פופולרי
  • משתמשים
  • תרומות לאוצריא
  • צור קשר
  • דף הבית
  • חוקי הפורום
  • מדריכים
  • פוסטים אחרונים
  • לא נפתר
  • תגיות
  • פופולרי
  • משתמשים
  • תרומות לאוצריא
  • צור קשר
עיצובים
  • בהיר
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • כהה
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • ברירת מחדל (ללא עיצוב (ברירת מחדל))
  • ללא עיצוב (ברירת מחדל)
כיווץ
לוגו אתר

פורום אוצריא

אוצריא - דף הבית
|
קח שותפות בהוספת ספרים
|
תרום לאוצריא
  1. דף הבית
  2. מדריכים ותוכנות עזר לאוצריא
  3. להורדה | ~~רציתם תוספים לתוכנה? קיבלתם!...~~

להורדה | ~~רציתם תוספים לתוכנה? קיבלתם!...~~

מתוזמן נעוץ נעול הועבר מדריכים ותוכנות עזר לאוצריא
תוספיםתכנותמדריך
52 פוסטים 13 כותבים 2.2k צפיות 11 עוקבים
  • מהישן לחדש
  • מהחדש לישן
  • הכי הרבה הצבעות
    תגובה
    • תגובה כנושא
    התחברו כדי לפרסם תגובה
    נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
    • יאיר דניאלי
      יאיר דניאלי
      יאיר דניאל
      תורם לאוצריא ניהול תוספים
      כתב ב נערך לאחרונה על ידי יאיר דניאל
      #1

      בעקבות בקשות ודיונים שונים בפורום - בנוגע לכך האם אפשר שיהיה תוספים לתוכנה - שדרכם יוכלו להוריד מאגרים חיצוניים כמו שלי ושל @שמואל5 - בניתי (-כמובן בעזרת AI...) סוג של תוסף לתוכנה - היא כעת בהרצה, ועוד מעט תעלה.
      פרטים בספויילר הראשון:

      אין אפשרות להוסיף תוספים חיצוניים לתוכנה שכתובה בפלאטר ודארט, מה שכן ישנם 3 אפשרויות:

      **1. שימוש ב-Dynamic Loading (טעינה דינמית)
      זו הדרך הנפוצה ביותר בתוכנות שולחניות (Desktop). ניתן לבנות את האפליקציה כך שהיא תחפש קבצי ספרייה חיצוניים (כמו .dll בחלונות או .so בלינוקס) בתיקייה מסוימת בזמן ריצה.

      איך זה עובד: משתמשים בחבילת dart:ffi כדי לקרוא לפונקציות מתוך קבצי ה-C/C++ החיצוניים.

      הוספת כפתורים: התוסף יכול להחזיר נתונים שהתוכנה הראשית מפרשת כהוראה להציג כפתור או פונקציה חדשה.

      1. הזרקת קוד באמצעות מנוע Scripting
        זוהי הדרך הגמישה ביותר להוספת פונקציונליות ושינוי ממשק המשתמש (UI) ללא צורך בקימפול.

      שילוב מנוע לואה (Lua) או JavaScript: ניתן להטמיע בתוך אפליקציית ה-Flutter מנוע הרצה שמפרש קבצי טקסט חיצוניים.

      דוגמה: התוסף יהיה קובץ .lua המגדיר פונקציות ומיקומים של כפתורים. האפליקציה קוראת את הקובץ ובונה את ה-UI בהתאם.

      1. ארכיטקטורת Micro-Frontends (באמצעות נתיבים דינמיים)
        אם מדובר בתוספים שאתה מפתח בעצמך ורק רוצה לאפשר למשתמש לבחור אם להתקין אותם:

      ניתן להשתמש בטכניקה של Deferred Loading. זה מאפשר להוריד חלקי קוד מהרשת רק כשהמשתמש זקוק להם, מה שחוסך במקום ומאפשר הוספת רכיבים בצורה מודולרית.**

      עד כאן תשובותו של הבן הרשע, סליחה של ג'מיני...

      למעשה מה שעשיתי זה כך:
      ישנה תוכנה בשם AutoHotkey שמאפשרת להזריק סקריפטים מעל תוכנות קיימות, וליצור כפתורים צפים עליהם.
      כתבתי בעזרת הAI קוד פייתון וקוד AHK (הקוד שאיתו התוכנה הזו עובדת) שמה שהם עושים ביחד הוא כך - התוכנה רצה ברקע במחשב (היא לא לוקחת זיכרון - בקושי 2M) וכשאוצריא עולה - מיד צץ כפתור חדש בסרגל - עם אייקון של הורדה, וטולטיפ של הסבר על הכפתור.
      לחיצה עליו פותחת חלון ששואל האם להוריד את המאגר שלי - לתוך אוצריא, ובלחיצה על אישור - נפתח חלון התקדמות שבסופו ההודעה "הספרים הותקנו בהצלחה"
      עיינו בספויילר הבא - לתמונות.

      תמונות:

      1.png
      11.png
      2.png
      3.png

      לאחר שעות ששרפתי על זה - ובאגים בלי סוף - נמאס לי מהעניין והחלטתי שאני פורש.

      מצורף הקודים למי שרוצה להמשיך את זה מכאן.

      בהצלחה, וסליחה מכל מי שפיתחתי בו ציפיות.
      הקודים להורדה:
      downloader.py
      OtzariaButton (1).ahk
      (יש להתקין את הספריה הבאה:)

      pip install requests
      

      קוד הפייתון (- שעליו רץ הקוד השני)

      import requests
      import sys
      import zipfile
      import shutil
      from pathlib import Path
      
      # הגדרות מערכת - נשאר בדיוק לפי ההנחיות שלך
      API_URL = "https://api.github.com/repos/YairDaniel123/Otzarya-Library/releases/latest"
      OTZARIA_LIB = Path(r"C:\ProgramData\otzaria\books")
      TEMP_FILE = Path("download.tmp")
      HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
      CHUNK_SIZE = 1024 * 1024
      
      def get_remote_assets():
          try:
              r = requests.get(API_URL, headers=HEADERS, timeout=15)
              r.raise_for_status()
              data = r.json()
              assets = {}
              for a in data.get("assets", []):
                  if not a["name"].endswith(".zip"):
                      continue
                  name = a.get("label") or a["name"]
                  assets[name] = {
                      "url": a["browser_download_url"],
                      "updated": a["updated_at"],
                      "is_full": (a["name"] == "full-library.zip")
                  }
              return assets
          except Exception as e:
              print(f"ERROR:LIST|{str(e)}", flush=True)
              sys.exit(1)
      
      def check_updates():
          if not OTZARIA_LIB.exists():
              try:
                  OTZARIA_LIB.mkdir(parents=True, exist_ok=True)
              except Exception as e:
                  print(f"ERROR:PERMISSION|{str(e)}", flush=True)
                  return
      
          remote = get_remote_assets()
          results = []
          
          for name, info in remote.items():
              folder = name.replace(".zip", "")
              version_file = OTZARIA_LIB / folder / ".version_info"
              
              is_up_to_date = version_file.exists() and version_file.read_text(encoding="utf-8").strip() == info["updated"]
              
              if not is_up_to_date:
                  type_id = "0" if info["is_full"] else "1"
                  results.append(f"{type_id}|{name}|{info['url']}|{info['updated']}")
          
          if results:
              results.sort()
              print(f"LIST:{';'.join(results)}", flush=True)
          else:
              print("EMPTY", flush=True)
      
      def download_and_extract(url, updated_at, display_name):
          try:
              with requests.get(url, stream=True, headers=HEADERS, timeout=(10, None)) as r:
                  r.raise_for_status()
                  total = int(r.headers.get("content-length", 0))
                  done = 0
                  with open(TEMP_FILE, "wb") as f:
                      for chunk in r.iter_content(CHUNK_SIZE):
                          if chunk:
                              f.write(chunk)
                              done += len(chunk)
                              if total:
                                  print(f"DL_PROGRESS:{int(done/total*100)}", flush=True)
              
              print("DL_FINISHED", flush=True)
              folder_name = display_name.replace(".zip", "")
              target_dir = OTZARIA_LIB / folder_name
              temp_extract = OTZARIA_LIB / f"temp_{folder_name}"
              
              if temp_extract.exists(): shutil.rmtree(temp_extract)
              temp_extract.mkdir(parents=True)
              
              with zipfile.ZipFile(TEMP_FILE, "r") as z:
                  files = [f for f in z.namelist() if not f.endswith(".version_info")]
                  for i, f in enumerate(files):
                      z.extract(f, temp_extract)
                      print(f"EX_PROGRESS:{int((i+1)/len(files)*100)}", flush=True)
              
              if target_dir.exists(): shutil.rmtree(target_dir)
              temp_extract.rename(target_dir)
              (target_dir / ".version_info").write_text(updated_at, encoding="utf-8")
              print("DONE", flush=True)
              
          except Exception as e:
              print(f"ERROR:PROCESS|{str(e)}", flush=True)
          finally:
              if TEMP_FILE.exists(): TEMP_FILE.unlink()
      
      if __name__ == "__main__":
          if len(sys.argv) < 2: sys.exit()
          cmd = sys.argv[1]
          if cmd == "CHECK": check_updates()
          elif "|" in cmd:
              p = cmd.split("|")
              if len(p) >= 4: download_and_extract(p[2], p[3], p[1])
      

      קוד AHK (יש צורך להתקין את התוכנה הזו בשביל להריץ את הקוד, תוכנה קטנה ממש).

      #Requires AutoHotkey v2.0
      #SingleInstance Force
      
      global MainColor   := "F8F4E8"   
      global AccentColor := "7D5800"   
      global BgColor     := "EDE8D5"   
      global IsHover     := false
      global Queue       := []
      global Worker      := ""
      global IsCancelled := false
      global OurWindows  := Map()      
      global SelectGui   := unset
      global ProgGui     := unset
      global MsgGui      := unset
      
      ; =====================================================
      ;  גרירת חלונות
      ; =====================================================
      OnMessage(0x0201, WM_LBUTTONDOWN)
      WM_LBUTTONDOWN(wParam, lParam, msg, hwnd) {
          rootHwnd := DllCall("GetAncestor", "ptr", hwnd, "uint", 2, "ptr")
          if !OurWindows.Has(rootHwnd)
              return
          try {
              cls := WinGetClass("ahk_id " hwnd)
              if (cls = "Button" || cls = "Edit")
                  return
          }
          PostMessage(0xA1, 2,,, "ahk_id " rootHwnd)
      }
      
      ApplyRounding(guiObj) {
          guiObj.Opt("+LastFound")
          DllCall("dwmapi\DwmSetWindowAttribute", "ptr", guiObj.Hwnd, "uint", 33, "ptr*", 2, "uint", 4)
      }
      
      ; =====================================================
      ;  כפתור הורדה צף
      ; =====================================================
      BtnGui := Gui("+AlwaysOnTop -Caption +ToolWindow +LastFound")
      BtnGui.BackColor := "EEAA99"
      WinSetTransColor("EEAA99", BtnGui)
      BtnGui.SetFont("s22", "Segoe UI Symbol")
      global BtnTxt := BtnGui.Add("Text", "x0 y0 w60 h50 Center c" AccentColor, "📥")
      BtnTxt.OnEvent("Click", ShowSelectionMenu)
      OurWindows[BtnGui.Hwnd] := true
      
      TipGui := Gui("-Caption +AlwaysOnTop +ToolWindow")
      TipGui.BackColor := "2B2B2B"
      TipGui.SetFont("s9 w800 cWhite", "Segoe UI")
      TipGui.Add("Text", "x8 y4 w80 h44 Center", "הורדת מאגרי ספרים מותאמים")
      ApplyRounding(TipGui)
      OurWindows[TipGui.Hwnd] := true
      
      SetTimer(WatchOtzaria, 250)
      
      WatchOtzaria() {
          global IsHover
          TargetID := WinExist("ahk_exe otzaria.exe")
          ActiveID := WinActive("A")
      
          SelectHwnd := (IsSet(SelectGui) && IsObject(SelectGui)) ? SelectGui.Hwnd : 0
          ProgHwnd   := (IsSet(ProgGui) && IsObject(ProgGui)) ? ProgGui.Hwnd : 0
          MsgHwnd    := (IsSet(MsgGui) && IsObject(MsgGui)) ? MsgGui.Hwnd : 0
      
          ; בדיקה האם אנחנו עובדים כרגע על אוצריא או על אחד החלונות שלנו
          IsOtzariaContext := TargetID && (ActiveID = TargetID || ActiveID = SelectHwnd || ActiveID = BtnGui.Hwnd || ActiveID = ProgHwnd || ActiveID = MsgHwnd)
      
          if IsOtzariaContext {
              WinGetPos(&X, &Y, &W, &H, "ahk_id " TargetID)
              btnX := X + W - 78
              btnY := Y + H - 186
              
              BtnGui.Show("x" btnX " y" btnY " w60 h50 NoActivate")
      
              ; החזרת החלונות במידה וחזרנו לאוצריא מתוכנה אחרת
              if SelectHwnd
                  try SelectGui.Show("NoActivate")
              if ProgHwnd
                  try ProgGui.Show("NoActivate")
      
              MouseGetPos(,, &HoverWin)
              if (HoverWin = BtnGui.Hwnd) {
                  if !IsHover {
                      TipGui.Show("x" (btnX - 85) " y" (btnY + 5) " NoActivate")
                      IsHover := true
                  }
              } else if IsHover {
                  TipGui.Hide()
                  IsHover := false
              }
          } else {
              BtnGui.Hide()
              TipGui.Hide()
              ; הסתרת חלונות בעת מעבר לתוכנה אחרת (לא סגירה, רק הסתרה)
              if SelectHwnd
                  try SelectGui.Hide()
              if ProgHwnd
                  try ProgGui.Hide()
          }
      }
      
      ; =====================================================
      ;  חלון בחירת ספרים מעוצב וימין-שמאל (RTL)
      ; =====================================================
      ShowSelectionMenu(*) {
          BtnTxt.Text := "⏳"
          try {
              shell := ComObject("WScript.Shell")
              exec  := shell.Exec("pythonw.exe downloader.py CHECK")
              res   := exec.StdOut.ReadAll()
          } catch {
              BtnTxt.Text := "📥"
              CustomMsgBox("שגיאה: לא ניתן להפעיל את פייתון.", "שגיאת הפעלה")
              return
          }
          BtnTxt.Text := "📥"
      
          if !InStr(res, "LIST:") || InStr(res, "EMPTY") {
              CustomMsgBox("המאגר מעודכן!", "עדכון ספריה")
              return
          }
      
          global SelectGui := Gui("+AlwaysOnTop -Caption")
          SelectGui.BackColor := MainColor
          ApplyRounding(SelectGui)
          OurWindows[SelectGui.Hwnd] := true
      
          SelectGui.SetFont("s15 w700 c" AccentColor, "Segoe UI")
          SelectGui.Add("Text", "x0 y15 w420 Center", "עדכון ספריה")
          
          SelectGui.SetFont("s11 w600 cBlack", "Segoe UI")
          ; +0x20 ו-Right אחראים על הפיכת הצ'קבוקס לימין שמאלה
          cbAll := SelectGui.Add("Checkbox", "x20 y+20 w380 Right +0x20", "בחר / בטל הכל (ללא המאגר המלא)")
      
          Checkboxes := []
          items := StrSplit(StrReplace(res, "LIST:", ""), ";")
          for item in items {
              if (Trim(item) = "") {
                  continue 
              }
              p := StrSplit(item, "|")
              isFull := (p[1] = "0")
              SelectGui.SetFont("s11 w400 cBlack", "Segoe UI")
              cb := SelectGui.Add("Checkbox", (p[1] = "1" ? "Checked " : "") "x20 y+10 w380 Right +0x20", p[2])
              Checkboxes.Push({full: item, cb: cb, isFull: isFull})
          }
      
          cbAll.OnEvent("Click", (self, *) => ToggleAll(self.Value, Checkboxes))
      
          BtnDown   := SelectGui.Add("Button", "Default w160 h35 x40 y+25", "הורד")
          BtnCancel := SelectGui.Add("Button", "w160 h35 x+20", "ביטול")
      
          BtnDown.OnEvent("Click", (*) => StartSelectedDownload(SelectGui, Checkboxes))
          BtnCancel.OnEvent("Click", (*) => SelectGui.Destroy())
      
          SelectGui.Show("w420 Center")
      }
      
      ToggleAll(val, Checkboxes) {
          for item in Checkboxes {
              if !item.isFull
                  item.cb.Value := val
          }
      }
      
      ; =====================================================
      ;  הודעה מעוצבת בסגנון אוצריא
      ; =====================================================
      CustomMsgBox(txt, title) {
          global MsgGui := Gui("-Caption +AlwaysOnTop +Owner")
          MsgGui.BackColor := MainColor
          ApplyRounding(MsgGui)
          OurWindows[MsgGui.Hwnd] := true
      
          MsgGui.SetFont("s15 w700 c" AccentColor, "Segoe UI")
          MsgGui.Add("Text", "x0 y20 w350 Center", title)
      
          MsgGui.SetFont("s11 w400 cBlack", "Segoe UI")
          MsgGui.Add("Text", "x25 y+15 w300 Center +Wrap", txt)
      
          BtnOk := MsgGui.Add("Button", "Default w120 h35 x115 y+25", "אישור")
          BtnOk.OnEvent("Click", (*) => MsgGui.Destroy())
      
          MsgGui.Show("w350 Center")
      }
      
      ; =====================================================
      ;  חלון התקדמות
      ; =====================================================
      ProgressShow(FileName, Remaining) {
          global ProgGui, StatusTxt, Bar, PctLabel
      
          ProgGui := Gui("-Caption +AlwaysOnTop")
          ProgGui.BackColor := MainColor
          ApplyRounding(ProgGui)
          OurWindows[ProgGui.Hwnd] := true
      
          ProgGui.SetFont("s14 w700 c" AccentColor, "Segoe UI")
          ProgGui.Add("Text", "x0 y20 w400 Center", "הורדה בתהליך")
      
          ProgGui.SetFont("s11 w600 c" AccentColor, "Segoe UI")
          ProgGui.Add("Text", "x25 y+15 w350 Center +Wrap", FileName)
      
          ProgGui.SetFont("s12 w700 c" AccentColor, "Segoe UI")
          StatusTxt := ProgGui.Add("Text", "x0 y+15 w400 Center", "מוריד...")
      
          Bar := ProgGui.Add("Progress", "x30 y+12 w340 h18 cGreen Background" BgColor, 0)
      
          ProgGui.SetFont("s11 w700 cBlack", "Segoe UI")
          PctLabel := ProgGui.Add("Text", "x0 y+8 w400 Center", "0%")
      
          CancelBtn := ProgGui.Add("Button", "w120 h35 x140 y+20", "ביטול")
          CancelBtn.OnEvent("Click", CancelDownload)
      
          ProgGui.Show("w400 Center")
      }
      
      ; =====================================================
      ;  ניהול תור הורדות
      ; =====================================================
      StartSelectedDownload(GuiObj, Checkboxes) {
          global Queue := [], IsCancelled := false
          for item in Checkboxes {
              if item.cb.Value
                  Queue.Push(item.full)
          }
          GuiObj.Destroy()
          if Queue.Length > 0
              NextDownload()
          else
              CustomMsgBox("לא נבחרו ספרים להורדה.", "שים לב")
      }
      
      NextDownload() {
          global Queue, IsCancelled, Worker
          if IsCancelled || (Queue.Length = 0) {
              if !IsCancelled
                  CustomMsgBox("הספריה עודכנה בהצלחה!", "סיום הורדה")
              return
          }
          CurrentItem := Queue.RemoveAt(1)
          p := StrSplit(CurrentItem, "|")
          ProgressShow(p[2], Queue.Length)
          Worker := ComObject("WScript.Shell").Exec('pythonw.exe downloader.py "' CurrentItem '"')
          SetTimer(CheckStatus, 100)
      }
      
      CheckStatus() {
          global Worker, ProgGui, StatusTxt, Bar, PctLabel
      
          if Worker.StdOut.AtEndOfStream {
              SetTimer(CheckStatus, 0)
              try {
                  if IsObject(ProgGui) && (ProgGui != "") {
                      ProgGui.Destroy()
                      ProgGui := unset
                      CustomMsgBox("ההורדה נכשלה.`nבדוק חיבור לאינטרנט.", "שגיאה")
                  }
              }
              return
          }
      
          line := Worker.StdOut.ReadLine()
          if InStr(line, "DL_PROGRESS:") {
              pct := StrReplace(line, "DL_PROGRESS:", "")
              Bar.Value     := pct
              PctLabel.Text := pct "%"
          } else if InStr(line, "DL_FINISHED") {
              StatusTxt.Text := "מחלץ קבצים..."
              Bar.Value := 0
          } else if InStr(line, "EX_PROGRESS:") {
              pct := StrReplace(line, "EX_PROGRESS:", "")
              Bar.Value     := pct
              PctLabel.Text := pct "%"
          } else if InStr(line, "DONE") {
              SetTimer(CheckStatus, 0)
              ProgGui.Destroy()
              ProgGui := unset
              NextDownload()
          }
      }
      
      CancelDownload(*) {
          global Worker, IsCancelled, ProgGui
          IsCancelled := true
          SetTimer(CheckStatus, 0)
          try Worker.Terminate()
          ProgGui.Destroy()
          ProgGui := unset
          CustomMsgBox("ההורדה בוטלה.", "ביטול")
      }
      

      "כִּי רָצוּ עֲבָדֶיךָ אֶת אֲבָנֶיהָ וְאֶת עֲפָרָהּ יְחֹנֵנוּ"

      הבל הבליםה דנדיד u88U יאיר דניאלי 4 תגובות תגובה אחרונה
      3
      • יאיר דניאלי יאיר דניאל

        בעקבות בקשות ודיונים שונים בפורום - בנוגע לכך האם אפשר שיהיה תוספים לתוכנה - שדרכם יוכלו להוריד מאגרים חיצוניים כמו שלי ושל @שמואל5 - בניתי (-כמובן בעזרת AI...) סוג של תוסף לתוכנה - היא כעת בהרצה, ועוד מעט תעלה.
        פרטים בספויילר הראשון:

        אין אפשרות להוסיף תוספים חיצוניים לתוכנה שכתובה בפלאטר ודארט, מה שכן ישנם 3 אפשרויות:

        **1. שימוש ב-Dynamic Loading (טעינה דינמית)
        זו הדרך הנפוצה ביותר בתוכנות שולחניות (Desktop). ניתן לבנות את האפליקציה כך שהיא תחפש קבצי ספרייה חיצוניים (כמו .dll בחלונות או .so בלינוקס) בתיקייה מסוימת בזמן ריצה.

        איך זה עובד: משתמשים בחבילת dart:ffi כדי לקרוא לפונקציות מתוך קבצי ה-C/C++ החיצוניים.

        הוספת כפתורים: התוסף יכול להחזיר נתונים שהתוכנה הראשית מפרשת כהוראה להציג כפתור או פונקציה חדשה.

        1. הזרקת קוד באמצעות מנוע Scripting
          זוהי הדרך הגמישה ביותר להוספת פונקציונליות ושינוי ממשק המשתמש (UI) ללא צורך בקימפול.

        שילוב מנוע לואה (Lua) או JavaScript: ניתן להטמיע בתוך אפליקציית ה-Flutter מנוע הרצה שמפרש קבצי טקסט חיצוניים.

        דוגמה: התוסף יהיה קובץ .lua המגדיר פונקציות ומיקומים של כפתורים. האפליקציה קוראת את הקובץ ובונה את ה-UI בהתאם.

        1. ארכיטקטורת Micro-Frontends (באמצעות נתיבים דינמיים)
          אם מדובר בתוספים שאתה מפתח בעצמך ורק רוצה לאפשר למשתמש לבחור אם להתקין אותם:

        ניתן להשתמש בטכניקה של Deferred Loading. זה מאפשר להוריד חלקי קוד מהרשת רק כשהמשתמש זקוק להם, מה שחוסך במקום ומאפשר הוספת רכיבים בצורה מודולרית.**

        עד כאן תשובותו של הבן הרשע, סליחה של ג'מיני...

        למעשה מה שעשיתי זה כך:
        ישנה תוכנה בשם AutoHotkey שמאפשרת להזריק סקריפטים מעל תוכנות קיימות, וליצור כפתורים צפים עליהם.
        כתבתי בעזרת הAI קוד פייתון וקוד AHK (הקוד שאיתו התוכנה הזו עובדת) שמה שהם עושים ביחד הוא כך - התוכנה רצה ברקע במחשב (היא לא לוקחת זיכרון - בקושי 2M) וכשאוצריא עולה - מיד צץ כפתור חדש בסרגל - עם אייקון של הורדה, וטולטיפ של הסבר על הכפתור.
        לחיצה עליו פותחת חלון ששואל האם להוריד את המאגר שלי - לתוך אוצריא, ובלחיצה על אישור - נפתח חלון התקדמות שבסופו ההודעה "הספרים הותקנו בהצלחה"
        עיינו בספויילר הבא - לתמונות.

        תמונות:

        1.png
        11.png
        2.png
        3.png

        לאחר שעות ששרפתי על זה - ובאגים בלי סוף - נמאס לי מהעניין והחלטתי שאני פורש.

        מצורף הקודים למי שרוצה להמשיך את זה מכאן.

        בהצלחה, וסליחה מכל מי שפיתחתי בו ציפיות.
        הקודים להורדה:
        downloader.py
        OtzariaButton (1).ahk
        (יש להתקין את הספריה הבאה:)

        pip install requests
        

        קוד הפייתון (- שעליו רץ הקוד השני)

        import requests
        import sys
        import zipfile
        import shutil
        from pathlib import Path
        
        # הגדרות מערכת - נשאר בדיוק לפי ההנחיות שלך
        API_URL = "https://api.github.com/repos/YairDaniel123/Otzarya-Library/releases/latest"
        OTZARIA_LIB = Path(r"C:\ProgramData\otzaria\books")
        TEMP_FILE = Path("download.tmp")
        HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
        CHUNK_SIZE = 1024 * 1024
        
        def get_remote_assets():
            try:
                r = requests.get(API_URL, headers=HEADERS, timeout=15)
                r.raise_for_status()
                data = r.json()
                assets = {}
                for a in data.get("assets", []):
                    if not a["name"].endswith(".zip"):
                        continue
                    name = a.get("label") or a["name"]
                    assets[name] = {
                        "url": a["browser_download_url"],
                        "updated": a["updated_at"],
                        "is_full": (a["name"] == "full-library.zip")
                    }
                return assets
            except Exception as e:
                print(f"ERROR:LIST|{str(e)}", flush=True)
                sys.exit(1)
        
        def check_updates():
            if not OTZARIA_LIB.exists():
                try:
                    OTZARIA_LIB.mkdir(parents=True, exist_ok=True)
                except Exception as e:
                    print(f"ERROR:PERMISSION|{str(e)}", flush=True)
                    return
        
            remote = get_remote_assets()
            results = []
            
            for name, info in remote.items():
                folder = name.replace(".zip", "")
                version_file = OTZARIA_LIB / folder / ".version_info"
                
                is_up_to_date = version_file.exists() and version_file.read_text(encoding="utf-8").strip() == info["updated"]
                
                if not is_up_to_date:
                    type_id = "0" if info["is_full"] else "1"
                    results.append(f"{type_id}|{name}|{info['url']}|{info['updated']}")
            
            if results:
                results.sort()
                print(f"LIST:{';'.join(results)}", flush=True)
            else:
                print("EMPTY", flush=True)
        
        def download_and_extract(url, updated_at, display_name):
            try:
                with requests.get(url, stream=True, headers=HEADERS, timeout=(10, None)) as r:
                    r.raise_for_status()
                    total = int(r.headers.get("content-length", 0))
                    done = 0
                    with open(TEMP_FILE, "wb") as f:
                        for chunk in r.iter_content(CHUNK_SIZE):
                            if chunk:
                                f.write(chunk)
                                done += len(chunk)
                                if total:
                                    print(f"DL_PROGRESS:{int(done/total*100)}", flush=True)
                
                print("DL_FINISHED", flush=True)
                folder_name = display_name.replace(".zip", "")
                target_dir = OTZARIA_LIB / folder_name
                temp_extract = OTZARIA_LIB / f"temp_{folder_name}"
                
                if temp_extract.exists(): shutil.rmtree(temp_extract)
                temp_extract.mkdir(parents=True)
                
                with zipfile.ZipFile(TEMP_FILE, "r") as z:
                    files = [f for f in z.namelist() if not f.endswith(".version_info")]
                    for i, f in enumerate(files):
                        z.extract(f, temp_extract)
                        print(f"EX_PROGRESS:{int((i+1)/len(files)*100)}", flush=True)
                
                if target_dir.exists(): shutil.rmtree(target_dir)
                temp_extract.rename(target_dir)
                (target_dir / ".version_info").write_text(updated_at, encoding="utf-8")
                print("DONE", flush=True)
                
            except Exception as e:
                print(f"ERROR:PROCESS|{str(e)}", flush=True)
            finally:
                if TEMP_FILE.exists(): TEMP_FILE.unlink()
        
        if __name__ == "__main__":
            if len(sys.argv) < 2: sys.exit()
            cmd = sys.argv[1]
            if cmd == "CHECK": check_updates()
            elif "|" in cmd:
                p = cmd.split("|")
                if len(p) >= 4: download_and_extract(p[2], p[3], p[1])
        

        קוד AHK (יש צורך להתקין את התוכנה הזו בשביל להריץ את הקוד, תוכנה קטנה ממש).

        #Requires AutoHotkey v2.0
        #SingleInstance Force
        
        global MainColor   := "F8F4E8"   
        global AccentColor := "7D5800"   
        global BgColor     := "EDE8D5"   
        global IsHover     := false
        global Queue       := []
        global Worker      := ""
        global IsCancelled := false
        global OurWindows  := Map()      
        global SelectGui   := unset
        global ProgGui     := unset
        global MsgGui      := unset
        
        ; =====================================================
        ;  גרירת חלונות
        ; =====================================================
        OnMessage(0x0201, WM_LBUTTONDOWN)
        WM_LBUTTONDOWN(wParam, lParam, msg, hwnd) {
            rootHwnd := DllCall("GetAncestor", "ptr", hwnd, "uint", 2, "ptr")
            if !OurWindows.Has(rootHwnd)
                return
            try {
                cls := WinGetClass("ahk_id " hwnd)
                if (cls = "Button" || cls = "Edit")
                    return
            }
            PostMessage(0xA1, 2,,, "ahk_id " rootHwnd)
        }
        
        ApplyRounding(guiObj) {
            guiObj.Opt("+LastFound")
            DllCall("dwmapi\DwmSetWindowAttribute", "ptr", guiObj.Hwnd, "uint", 33, "ptr*", 2, "uint", 4)
        }
        
        ; =====================================================
        ;  כפתור הורדה צף
        ; =====================================================
        BtnGui := Gui("+AlwaysOnTop -Caption +ToolWindow +LastFound")
        BtnGui.BackColor := "EEAA99"
        WinSetTransColor("EEAA99", BtnGui)
        BtnGui.SetFont("s22", "Segoe UI Symbol")
        global BtnTxt := BtnGui.Add("Text", "x0 y0 w60 h50 Center c" AccentColor, "📥")
        BtnTxt.OnEvent("Click", ShowSelectionMenu)
        OurWindows[BtnGui.Hwnd] := true
        
        TipGui := Gui("-Caption +AlwaysOnTop +ToolWindow")
        TipGui.BackColor := "2B2B2B"
        TipGui.SetFont("s9 w800 cWhite", "Segoe UI")
        TipGui.Add("Text", "x8 y4 w80 h44 Center", "הורדת מאגרי ספרים מותאמים")
        ApplyRounding(TipGui)
        OurWindows[TipGui.Hwnd] := true
        
        SetTimer(WatchOtzaria, 250)
        
        WatchOtzaria() {
            global IsHover
            TargetID := WinExist("ahk_exe otzaria.exe")
            ActiveID := WinActive("A")
        
            SelectHwnd := (IsSet(SelectGui) && IsObject(SelectGui)) ? SelectGui.Hwnd : 0
            ProgHwnd   := (IsSet(ProgGui) && IsObject(ProgGui)) ? ProgGui.Hwnd : 0
            MsgHwnd    := (IsSet(MsgGui) && IsObject(MsgGui)) ? MsgGui.Hwnd : 0
        
            ; בדיקה האם אנחנו עובדים כרגע על אוצריא או על אחד החלונות שלנו
            IsOtzariaContext := TargetID && (ActiveID = TargetID || ActiveID = SelectHwnd || ActiveID = BtnGui.Hwnd || ActiveID = ProgHwnd || ActiveID = MsgHwnd)
        
            if IsOtzariaContext {
                WinGetPos(&X, &Y, &W, &H, "ahk_id " TargetID)
                btnX := X + W - 78
                btnY := Y + H - 186
                
                BtnGui.Show("x" btnX " y" btnY " w60 h50 NoActivate")
        
                ; החזרת החלונות במידה וחזרנו לאוצריא מתוכנה אחרת
                if SelectHwnd
                    try SelectGui.Show("NoActivate")
                if ProgHwnd
                    try ProgGui.Show("NoActivate")
        
                MouseGetPos(,, &HoverWin)
                if (HoverWin = BtnGui.Hwnd) {
                    if !IsHover {
                        TipGui.Show("x" (btnX - 85) " y" (btnY + 5) " NoActivate")
                        IsHover := true
                    }
                } else if IsHover {
                    TipGui.Hide()
                    IsHover := false
                }
            } else {
                BtnGui.Hide()
                TipGui.Hide()
                ; הסתרת חלונות בעת מעבר לתוכנה אחרת (לא סגירה, רק הסתרה)
                if SelectHwnd
                    try SelectGui.Hide()
                if ProgHwnd
                    try ProgGui.Hide()
            }
        }
        
        ; =====================================================
        ;  חלון בחירת ספרים מעוצב וימין-שמאל (RTL)
        ; =====================================================
        ShowSelectionMenu(*) {
            BtnTxt.Text := "⏳"
            try {
                shell := ComObject("WScript.Shell")
                exec  := shell.Exec("pythonw.exe downloader.py CHECK")
                res   := exec.StdOut.ReadAll()
            } catch {
                BtnTxt.Text := "📥"
                CustomMsgBox("שגיאה: לא ניתן להפעיל את פייתון.", "שגיאת הפעלה")
                return
            }
            BtnTxt.Text := "📥"
        
            if !InStr(res, "LIST:") || InStr(res, "EMPTY") {
                CustomMsgBox("המאגר מעודכן!", "עדכון ספריה")
                return
            }
        
            global SelectGui := Gui("+AlwaysOnTop -Caption")
            SelectGui.BackColor := MainColor
            ApplyRounding(SelectGui)
            OurWindows[SelectGui.Hwnd] := true
        
            SelectGui.SetFont("s15 w700 c" AccentColor, "Segoe UI")
            SelectGui.Add("Text", "x0 y15 w420 Center", "עדכון ספריה")
            
            SelectGui.SetFont("s11 w600 cBlack", "Segoe UI")
            ; +0x20 ו-Right אחראים על הפיכת הצ'קבוקס לימין שמאלה
            cbAll := SelectGui.Add("Checkbox", "x20 y+20 w380 Right +0x20", "בחר / בטל הכל (ללא המאגר המלא)")
        
            Checkboxes := []
            items := StrSplit(StrReplace(res, "LIST:", ""), ";")
            for item in items {
                if (Trim(item) = "") {
                    continue 
                }
                p := StrSplit(item, "|")
                isFull := (p[1] = "0")
                SelectGui.SetFont("s11 w400 cBlack", "Segoe UI")
                cb := SelectGui.Add("Checkbox", (p[1] = "1" ? "Checked " : "") "x20 y+10 w380 Right +0x20", p[2])
                Checkboxes.Push({full: item, cb: cb, isFull: isFull})
            }
        
            cbAll.OnEvent("Click", (self, *) => ToggleAll(self.Value, Checkboxes))
        
            BtnDown   := SelectGui.Add("Button", "Default w160 h35 x40 y+25", "הורד")
            BtnCancel := SelectGui.Add("Button", "w160 h35 x+20", "ביטול")
        
            BtnDown.OnEvent("Click", (*) => StartSelectedDownload(SelectGui, Checkboxes))
            BtnCancel.OnEvent("Click", (*) => SelectGui.Destroy())
        
            SelectGui.Show("w420 Center")
        }
        
        ToggleAll(val, Checkboxes) {
            for item in Checkboxes {
                if !item.isFull
                    item.cb.Value := val
            }
        }
        
        ; =====================================================
        ;  הודעה מעוצבת בסגנון אוצריא
        ; =====================================================
        CustomMsgBox(txt, title) {
            global MsgGui := Gui("-Caption +AlwaysOnTop +Owner")
            MsgGui.BackColor := MainColor
            ApplyRounding(MsgGui)
            OurWindows[MsgGui.Hwnd] := true
        
            MsgGui.SetFont("s15 w700 c" AccentColor, "Segoe UI")
            MsgGui.Add("Text", "x0 y20 w350 Center", title)
        
            MsgGui.SetFont("s11 w400 cBlack", "Segoe UI")
            MsgGui.Add("Text", "x25 y+15 w300 Center +Wrap", txt)
        
            BtnOk := MsgGui.Add("Button", "Default w120 h35 x115 y+25", "אישור")
            BtnOk.OnEvent("Click", (*) => MsgGui.Destroy())
        
            MsgGui.Show("w350 Center")
        }
        
        ; =====================================================
        ;  חלון התקדמות
        ; =====================================================
        ProgressShow(FileName, Remaining) {
            global ProgGui, StatusTxt, Bar, PctLabel
        
            ProgGui := Gui("-Caption +AlwaysOnTop")
            ProgGui.BackColor := MainColor
            ApplyRounding(ProgGui)
            OurWindows[ProgGui.Hwnd] := true
        
            ProgGui.SetFont("s14 w700 c" AccentColor, "Segoe UI")
            ProgGui.Add("Text", "x0 y20 w400 Center", "הורדה בתהליך")
        
            ProgGui.SetFont("s11 w600 c" AccentColor, "Segoe UI")
            ProgGui.Add("Text", "x25 y+15 w350 Center +Wrap", FileName)
        
            ProgGui.SetFont("s12 w700 c" AccentColor, "Segoe UI")
            StatusTxt := ProgGui.Add("Text", "x0 y+15 w400 Center", "מוריד...")
        
            Bar := ProgGui.Add("Progress", "x30 y+12 w340 h18 cGreen Background" BgColor, 0)
        
            ProgGui.SetFont("s11 w700 cBlack", "Segoe UI")
            PctLabel := ProgGui.Add("Text", "x0 y+8 w400 Center", "0%")
        
            CancelBtn := ProgGui.Add("Button", "w120 h35 x140 y+20", "ביטול")
            CancelBtn.OnEvent("Click", CancelDownload)
        
            ProgGui.Show("w400 Center")
        }
        
        ; =====================================================
        ;  ניהול תור הורדות
        ; =====================================================
        StartSelectedDownload(GuiObj, Checkboxes) {
            global Queue := [], IsCancelled := false
            for item in Checkboxes {
                if item.cb.Value
                    Queue.Push(item.full)
            }
            GuiObj.Destroy()
            if Queue.Length > 0
                NextDownload()
            else
                CustomMsgBox("לא נבחרו ספרים להורדה.", "שים לב")
        }
        
        NextDownload() {
            global Queue, IsCancelled, Worker
            if IsCancelled || (Queue.Length = 0) {
                if !IsCancelled
                    CustomMsgBox("הספריה עודכנה בהצלחה!", "סיום הורדה")
                return
            }
            CurrentItem := Queue.RemoveAt(1)
            p := StrSplit(CurrentItem, "|")
            ProgressShow(p[2], Queue.Length)
            Worker := ComObject("WScript.Shell").Exec('pythonw.exe downloader.py "' CurrentItem '"')
            SetTimer(CheckStatus, 100)
        }
        
        CheckStatus() {
            global Worker, ProgGui, StatusTxt, Bar, PctLabel
        
            if Worker.StdOut.AtEndOfStream {
                SetTimer(CheckStatus, 0)
                try {
                    if IsObject(ProgGui) && (ProgGui != "") {
                        ProgGui.Destroy()
                        ProgGui := unset
                        CustomMsgBox("ההורדה נכשלה.`nבדוק חיבור לאינטרנט.", "שגיאה")
                    }
                }
                return
            }
        
            line := Worker.StdOut.ReadLine()
            if InStr(line, "DL_PROGRESS:") {
                pct := StrReplace(line, "DL_PROGRESS:", "")
                Bar.Value     := pct
                PctLabel.Text := pct "%"
            } else if InStr(line, "DL_FINISHED") {
                StatusTxt.Text := "מחלץ קבצים..."
                Bar.Value := 0
            } else if InStr(line, "EX_PROGRESS:") {
                pct := StrReplace(line, "EX_PROGRESS:", "")
                Bar.Value     := pct
                PctLabel.Text := pct "%"
            } else if InStr(line, "DONE") {
                SetTimer(CheckStatus, 0)
                ProgGui.Destroy()
                ProgGui := unset
                NextDownload()
            }
        }
        
        CancelDownload(*) {
            global Worker, IsCancelled, ProgGui
            IsCancelled := true
            SetTimer(CheckStatus, 0)
            try Worker.Terminate()
            ProgGui.Destroy()
            ProgGui := unset
            CustomMsgBox("ההורדה בוטלה.", "ביטול")
        }
        
        הבל הבליםה
        הבל הבליםה
        הבל הבלים
        צוות פיקוח
        כתב ב נערך לאחרונה על ידי
        #2

        @יאיר-דניאל כתב:

        C:\ProgramData\אוצריא\books

        עקרונית - C:\ProgramData\otzaria\books

        יאיר דניאלי תגובה 1 תגובה אחרונה
        0
        • הבל הבליםה הבל הבלים

          @יאיר-דניאל כתב:

          C:\ProgramData\אוצריא\books

          עקרונית - C:\ProgramData\otzaria\books

          יאיר דניאלי
          יאיר דניאלי
          יאיר דניאל
          תורם לאוצריא ניהול תוספים
          כתב ב נערך לאחרונה על ידי
          #3

          @הבל-הבלים אז למה מופיע לי התקייה שם בשם בעברית?

          "כִּי רָצוּ עֲבָדֶיךָ אֶת אֲבָנֶיהָ וְאֶת עֲפָרָהּ יְחֹנֵנוּ"

          הבל הבליםה תגובה 1 תגובה אחרונה
          0
          • יאיר דניאלי יאיר דניאל

            @הבל-הבלים אז למה מופיע לי התקייה שם בשם בעברית?

            הבל הבליםה
            הבל הבליםה
            הבל הבלים
            צוות פיקוח
            כתב ב נערך לאחרונה על ידי
            #4

            @יאיר-דניאל
            כי לא איפסת את התוכנה אחרי ההתקנה של 0.9.80 איפשהו שם שיניתי את זה, זה היה בעברית בטעות.

            יאיר דניאלי תגובה 1 תגובה אחרונה
            0
            • הבל הבליםה הבל הבלים

              @יאיר-דניאל
              כי לא איפסת את התוכנה אחרי ההתקנה של 0.9.80 איפשהו שם שיניתי את זה, זה היה בעברית בטעות.

              יאיר דניאלי
              יאיר דניאלי
              יאיר דניאל
              תורם לאוצריא ניהול תוספים
              כתב ב נערך לאחרונה על ידי
              #5

              @הבל-הבלים תודה, אוהב, דקל

              "כִּי רָצוּ עֲבָדֶיךָ אֶת אֲבָנֶיהָ וְאֶת עֲפָרָהּ יְחֹנֵנוּ"

              הבל הבליםה תגובה 1 תגובה אחרונה
              0
              • יאיר דניאלי יאיר דניאל

                @הבל-הבלים תודה, אוהב, דקל

                הבל הבליםה
                הבל הבליםה
                הבל הבלים
                צוות פיקוח
                כתב ב נערך לאחרונה על ידי
                #6

                > @יאיר-דניאל כתב:

                דקל

                ?

                יאיר דניאלי שמואל5ש 2 תגובות תגובה אחרונה
                0
                • הבל הבליםה הבל הבלים

                  > @יאיר-דניאל כתב:

                  דקל

                  ?

                  יאיר דניאלי
                  יאיר דניאלי
                  יאיר דניאל
                  תורם לאוצריא ניהול תוספים
                  כתב ב נערך לאחרונה על ידי
                  #7

                  @הבל-הבלים לא מכיר?
                  עשה חיפוש גוגל

                  "כִּי רָצוּ עֲבָדֶיךָ אֶת אֲבָנֶיהָ וְאֶת עֲפָרָהּ יְחֹנֵנוּ"

                  י. פל.י תגובה 1 תגובה אחרונה
                  0
                  • הבל הבליםה הבל הבלים

                    > @יאיר-דניאל כתב:

                    דקל

                    ?

                    שמואל5ש
                    שמואל5ש
                    שמואל5
                    כתב ב נערך לאחרונה על ידי
                    #8

                    @הבל-הבלים @יאיר-דניאל לי מופיע המיקום הזה... (איפסתי הגדרות בהתקנה, זה הגרסה האחרונה 0.9.86)
                    3381b715-d99a-47d1-8165-32e7869d21be-image.jpeg

                    יאיר דניאלי A 2 תגובות תגובה אחרונה
                    0
                    • שמואל5ש שמואל5

                      @הבל-הבלים @יאיר-דניאל לי מופיע המיקום הזה... (איפסתי הגדרות בהתקנה, זה הגרסה האחרונה 0.9.86)
                      3381b715-d99a-47d1-8165-32e7869d21be-image.jpeg

                      יאיר דניאלי
                      יאיר דניאלי
                      יאיר דניאל
                      תורם לאוצריא ניהול תוספים
                      כתב ב נערך לאחרונה על ידי יאיר דניאל
                      #9

                      @שמואל5 אין לי שם תיקייה בוקס, אבל עוד לא איפסתי כך שזה לא ראיה
                      עריכה: - אחרי איפוס - זה נמצא בנתיב שנתן @הבל-הבלים
                      @הבל-הבלים לגבי הוספת ספרים האם לשים בנתיב שאתה אמרת או שהוא?

                      "כִּי רָצוּ עֲבָדֶיךָ אֶת אֲבָנֶיהָ וְאֶת עֲפָרָהּ יְחֹנֵנוּ"

                      תגובה 1 תגובה אחרונה
                      0
                      • י. פל.י
                        י. פל.י
                        י. פל.
                        כתב ב נערך לאחרונה על ידי
                        #10

                        תייגת אותי, אז אענה:
                        https://github.com/Otzaria/otzaria/commit/9bd84f80cde68f92ef79514c7fdc3122c4e35c28
                        כרגע הכל תלוי ב @ori-bensimon
                        זה פיתוח שהוא ביקש, ואני מחכה לתגובתו.

                        ori bensimonO תגובה 1 תגובה אחרונה
                        0
                        • יאיר דניאלי יאיר דניאל

                          @הבל-הבלים לא מכיר?
                          עשה חיפוש גוגל

                          י. פל.י
                          י. פל.י
                          י. פל.
                          כתב ב נערך לאחרונה על ידי
                          #11

                          @יאיר-דניאל כתב:

                          עשה חיפוש גוגל

                          עשיתי, לא זכיתי להבין.

                          יאיר דניאלי תגובה 1 תגובה אחרונה
                          0
                          • י. פל.י י. פל.

                            תייגת אותי, אז אענה:
                            https://github.com/Otzaria/otzaria/commit/9bd84f80cde68f92ef79514c7fdc3122c4e35c28
                            כרגע הכל תלוי ב @ori-bensimon
                            זה פיתוח שהוא ביקש, ואני מחכה לתגובתו.

                            ori bensimonO
                            ori bensimonO
                            ori bensimon
                            כתב ב נערך לאחרונה על ידי
                            #12

                            @י.-פל. איך כבודי קשור לאירוע?

                            תגובה 1 תגובה אחרונה
                            0
                            • י. פל.י י. פל.

                              @יאיר-דניאל כתב:

                              עשה חיפוש גוגל

                              עשיתי, לא זכיתי להבין.

                              יאיר דניאלי
                              יאיר דניאלי
                              יאיר דניאל
                              תורם לאוצריא ניהול תוספים
                              כתב ב נערך לאחרונה על ידי
                              #13

                              @י.-פל. לכל מי שלא הבין...

                              לא היה לי כח לפרט - אז ביקשתי זאת מגמיני...

                              הביטוי "תודה אוהב דקל" הפך לוויראלי ברשתות החברתיות בישראל ועורר סקרנות רבה לגבי מקורו.

                              להלן הפרטים על השתלשלות הביטוי:

                              המקור
                              הביטוי הגיע מהזמר דקל וקנין, שהתפרסם כנער צעיר בזכות סרטוני "ברכות" אישיות שהיה שולח למעריצים ולחברים. בסיום כל סרטון ברכה שצילם, הוא נהג לחתום במשפט הקבוע והלבבי: "תודה, אוהב, דקל".

                              למה זה הפך לוויראלי?
                              השילוב בין הנימוס, הישירות והחתימה העצמית בגוף שלישי הפך לסימן ההיכר שלו. הציבור אימץ את המשפט כביטוי של חיבה, הומור עצמי או כדרך "לחתום" הודעות בצורה משעשעת.

                              השימוש כיום
                              כיום משתמשים בביטוי הזה כסוג של "סלנג" אינטרנטי, לעיתים גם על ידי אנשים שאינם קשורים לדקל וקנין, פשוט כדי להביע תודה בצורה קלילה שמתכתבת עם התרבות הדיגיטלית המקומית.

                              "כִּי רָצוּ עֲבָדֶיךָ אֶת אֲבָנֶיהָ וְאֶת עֲפָרָהּ יְחֹנֵנוּ"

                              תגובה 1 תגובה אחרונה
                              0
                              • יאיר דניאלי
                                יאיר דניאלי
                                יאיר דניאל
                                תורם לאוצריא ניהול תוספים
                                כתב ב נערך לאחרונה על ידי
                                #14

                                @י.-פל.
                                אגב, לא שאלתי לגבי ההכנסה של זה לתוכנה - לא רוצה לערב פרוייקטים חדשים לא קשורים.
                                סה"כ שאלתי האם זה הנתיב הנכון, והאם התוכנה תזהה כך את כל הספרים.
                                תודה בכ"א.

                                "כִּי רָצוּ עֲבָדֶיךָ אֶת אֲבָנֶיהָ וְאֶת עֲפָרָהּ יְחֹנֵנוּ"

                                תגובה 1 תגובה אחרונה
                                0
                                • חד בדראח
                                  חד בדראח
                                  חד בדרא
                                  כתב ב נערך לאחרונה על ידי
                                  #15

                                  @יאיר-דניאל
                                  תכלול בתוסף גם את המאגר של @שמואל5

                                  אתם לא בתחרות, נכון 🙂

                                  ואולי כבר לכל דרייב מזדמן, למה לא.. החל מספרי הגרחק שהעלו כאן וכלה ב...

                                  תגובה 1 תגובה אחרונה
                                  0
                                  • ד
                                    ד
                                    דבש
                                    כתב ב נערך לאחרונה על ידי
                                    #16

                                    יהיה נוח אם ניתן יהיה לבחור מה להוריד מהמאגר.
                                    אפשרי?

                                    יאיר דניאלי תגובה 1 תגובה אחרונה
                                    0
                                    • ד דבש

                                      יהיה נוח אם ניתן יהיה לבחור מה להוריד מהמאגר.
                                      אפשרי?

                                      יאיר דניאלי
                                      יאיר דניאלי
                                      יאיר דניאל
                                      תורם לאוצריא ניהול תוספים
                                      כתב ב נערך לאחרונה על ידי יאיר דניאל
                                      #17

                                      @דבש אולי אוסיף זאת עוד מעט, כעת זה בהרצה, בודק באגים מסויימים שקורים בנטפרי.

                                      עריכה:
                                      אולי אוסיף זאת עוד מעט
                                      נוסף!

                                      בודק באגים מסויימים שקורים בנטפרי
                                      נפתר!

                                      "כִּי רָצוּ עֲבָדֶיךָ אֶת אֲבָנֶיהָ וְאֶת עֲפָרָהּ יְחֹנֵנוּ"

                                      שמואל5ש תגובה 1 תגובה אחרונה
                                      2
                                      • יאיר דניאלי יאיר דניאל

                                        @דבש אולי אוסיף זאת עוד מעט, כעת זה בהרצה, בודק באגים מסויימים שקורים בנטפרי.

                                        עריכה:
                                        אולי אוסיף זאת עוד מעט
                                        נוסף!

                                        בודק באגים מסויימים שקורים בנטפרי
                                        נפתר!

                                        שמואל5ש
                                        שמואל5ש
                                        שמואל5
                                        כתב ב נערך לאחרונה על ידי
                                        #18

                                        @יאיר-דניאל יש לך צפי מתי זה ישוחרר?

                                        יאיר דניאלי תגובה 1 תגובה אחרונה
                                        0
                                        • שמואל5ש שמואל5

                                          @יאיר-דניאל יש לך צפי מתי זה ישוחרר?

                                          יאיר דניאלי
                                          יאיר דניאלי
                                          יאיר דניאל
                                          תורם לאוצריא ניהול תוספים
                                          כתב ב נערך לאחרונה על ידי
                                          #19

                                          @שמואל5 מקווה שבשעה הקרובה- עוד לא הוספתי את המאגר שלך, אחרי שזה יעבוד טוב, אוסיף בל"נ

                                          "כִּי רָצוּ עֲבָדֶיךָ אֶת אֲבָנֶיהָ וְאֶת עֲפָרָהּ יְחֹנֵנוּ"

                                          חד בדראח תגובה 1 תגובה אחרונה
                                          1
                                          • חנניהח
                                            חנניהח
                                            חנניה
                                            כתב ב נערך לאחרונה על ידי
                                            #20

                                            @יאיר-דניאל הוא יוריד כל פעם מחדש את כל המאגר, או שהוא יסנכרן כמו אוצריא עצמה?

                                            יאיר דניאלי תגובה 1 תגובה אחרונה
                                            0

                                            שלום! נראה שהשיחה הזו מעניינת אותך, אבל עדיין אין לך חשבון.

                                            נמאס לכם לגלול בין אותם הפוסטים בכל ביקור? כשנרשמים לחשבון, תמיד תחזרו בדיוק למקום שבו הייתם קודם, ותוכלו לבחור לקבל התראות על תגובות חדשות (בין אם במייל, ובין אם בהתראת פוש). תוכלו גם לשמור סימניות ולפרגן ב-upvote לפוסטים כדי להביע הערכה לחברי קהילה אחרים.

                                            בעזרת התרומה שלך, הפוסט הזה יכול להיות אפילו טוב יותר 💗

                                            הרשמה התחברות

                                            • התחברות

                                            • אין לך חשבון עדיין? הרשמה

                                            • התחברו או הירשמו כדי לחפש.
                                            • פוסט ראשון
                                              פוסט אחרון